Bug Summary

File:root/firefox-clang/intl/icu/source/common/cmemory.h
Warning:line 274, column 40
Returning null reference

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name locid.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/common -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/common -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D U_COMMON_IMPLEMENTATION -D _LIBCPP_DISABLE_DEPRECATION_WARNINGS -D U_USING_ICU_NAMESPACE=0 -D U_NO_DEFAULT_INCLUDE_UTF_HEADERS=1 -D U_HIDE_OBSOLETE_UTF_OLD_H=1 -D UCONFIG_NO_LEGACY_CONVERSION -D UCONFIG_NO_TRANSLITERATION -D UCONFIG_NO_REGULAR_EXPRESSIONS -D UCONFIG_NO_BREAK_ITERATION -D UCONFIG_NO_IDNA -D UCONFIG_NO_MF2 -D UCONFIG_NO_NORMALIZATION -D UCONFIG_NO_COLLATION -D U_CHARSET_IS_UTF8 -D UNISTR_FROM_CHAR_EXPLICIT=explicit -D UNISTR_FROM_STRING_EXPLICIT=explicit -D U_ENABLE_DYLOAD=0 -D U_DEBUG=1 -I /root/firefox-clang/config/external/icu/common -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/common -I /root/firefox-clang/intl/icu/source/i18n -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -Wno-comma -Wno-implicit-const-int-float-conversion -Wno-macro-redefined -Wno-microsoft-include -Wno-tautological-unsigned-enum-zero-compare -Wno-unreachable-code-loop-increment -Wno-unreachable-code-return -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ /root/firefox-clang/intl/icu/source/common/locid.cpp

/root/firefox-clang/intl/icu/source/common/locid.cpp

1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/*
4 **********************************************************************
5 * Copyright (C) 1997-2016, International Business Machines
6 * Corporation and others. All Rights Reserved.
7 **********************************************************************
8*
9* File locid.cpp
10*
11* Created by: Richard Gillam
12*
13* Modification History:
14*
15* Date Name Description
16* 02/11/97 aliu Changed gLocPath to fgDataDirectory and added
17* methods to get and set it.
18* 04/02/97 aliu Made operator!= inline; fixed return value
19* of getName().
20* 04/15/97 aliu Cleanup for AIX/Win32.
21* 04/24/97 aliu Numerous changes per code review.
22* 08/18/98 stephen Changed getDisplayName()
23* Added SIMPLIFIED_CHINESE, TRADITIONAL_CHINESE
24* Added getISOCountries(), getISOLanguages(),
25* getLanguagesForCountry()
26* 03/16/99 bertrand rehaul.
27* 07/21/99 stephen Added U_CFUNC setDefault
28* 11/09/99 weiv Added const char * getName() const;
29* 04/12/00 srl removing unicodestring api's and cached hash code
30* 08/10/01 grhoten Change the static Locales to accessor functions
31******************************************************************************
32*/
33
34#include <cstddef>
35#include <optional>
36#include <string_view>
37#include <type_traits>
38#include <utility>
39
40#include "unicode/bytestream.h"
41#include "unicode/locid.h"
42#include "unicode/localebuilder.h"
43#include "unicode/localpointer.h"
44#include "unicode/strenum.h"
45#include "unicode/stringpiece.h"
46#include "unicode/uloc.h"
47#include "unicode/ures.h"
48
49#include "bytesinkutil.h"
50#include "charstr.h"
51#include "charstrmap.h"
52#include "cmemory.h"
53#include "cstring.h"
54#include "fixedstring.h"
55#include "mutex.h"
56#include "putilimp.h"
57#include "uassert.h"
58#include "ucln_cmn.h"
59#include "uhash.h"
60#include "ulocimp.h"
61#include "umutex.h"
62#include "uniquecharstr.h"
63#include "ustr_imp.h"
64#include "uvector.h"
65
66U_NAMESPACE_BEGINnamespace icu_78 {
67
68static Locale *gLocaleCache = nullptr;
69static UInitOnce gLocaleCacheInitOnce {};
70
71// gDefaultLocaleMutex protects all access to gDefaultLocalesHashT and gDefaultLocale.
72static UMutex gDefaultLocaleMutex;
73static UHashtable *gDefaultLocalesHashT = nullptr;
74static Locale *gDefaultLocale = nullptr;
75
76/**
77 * \def ULOC_STRING_LIMIT
78 * strings beyond this value crash in CharString
79 */
80#define ULOC_STRING_LIMIT357913941 357913941
81
82U_NAMESPACE_END}
83
84typedef enum ELocalePos {
85 eENGLISH,
86 eFRENCH,
87 eGERMAN,
88 eITALIAN,
89 eJAPANESE,
90 eKOREAN,
91 eCHINESE,
92
93 eFRANCE,
94 eGERMANY,
95 eITALY,
96 eJAPAN,
97 eKOREA,
98 eCHINA, /* Alias for PRC */
99 eTAIWAN,
100 eUK,
101 eUS,
102 eCANADA,
103 eCANADA_FRENCH,
104 eROOT,
105
106
107 //eDEFAULT,
108 eMAX_LOCALES
109} ELocalePos;
110
111namespace {
112
113//
114// Deleter function for Locales owned by the default Locale hash table/
115//
116void U_CALLCONV
117deleteLocale(void *obj) {
118 delete static_cast<icu::Locale*>(obj);
119}
120
121UBool U_CALLCONV locale_cleanup()
122{
123 U_NAMESPACE_USEusing namespace icu_78;
124
125 delete [] gLocaleCache;
126 gLocaleCache = nullptr;
127 gLocaleCacheInitOnce.reset();
128
129 if (gDefaultLocalesHashT) {
130 uhash_close(gDefaultLocalesHashT); // Automatically deletes all elements, using deleter func.
131 gDefaultLocalesHashT = nullptr;
132 }
133 gDefaultLocale = nullptr;
134 return true;
135}
136
137void U_CALLCONV locale_init(UErrorCode &status) {
138 U_NAMESPACE_USEusing namespace icu_78;
139
140 U_ASSERT(gLocaleCache == nullptr)(static_cast <bool> (gLocaleCache == nullptr) ? void (0
) : __assert_fail ("gLocaleCache == nullptr", __builtin_FILE (
), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
141 gLocaleCache = new Locale[static_cast<int>(eMAX_LOCALES)];
142 if (gLocaleCache == nullptr) {
143 status = U_MEMORY_ALLOCATION_ERROR;
144 return;
145 }
146 ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup);
147 gLocaleCache[eROOT] = Locale("");
148 gLocaleCache[eENGLISH] = Locale("en");
149 gLocaleCache[eFRENCH] = Locale("fr");
150 gLocaleCache[eGERMAN] = Locale("de");
151 gLocaleCache[eITALIAN] = Locale("it");
152 gLocaleCache[eJAPANESE] = Locale("ja");
153 gLocaleCache[eKOREAN] = Locale("ko");
154 gLocaleCache[eCHINESE] = Locale("zh");
155 gLocaleCache[eFRANCE] = Locale("fr", "FR");
156 gLocaleCache[eGERMANY] = Locale("de", "DE");
157 gLocaleCache[eITALY] = Locale("it", "IT");
158 gLocaleCache[eJAPAN] = Locale("ja", "JP");
159 gLocaleCache[eKOREA] = Locale("ko", "KR");
160 gLocaleCache[eCHINA] = Locale("zh", "CN");
161 gLocaleCache[eTAIWAN] = Locale("zh", "TW");
162 gLocaleCache[eUK] = Locale("en", "GB");
163 gLocaleCache[eUS] = Locale("en", "US");
164 gLocaleCache[eCANADA] = Locale("en", "CA");
165 gLocaleCache[eCANADA_FRENCH] = Locale("fr", "CA");
166}
167
168} // namespace
169
170U_NAMESPACE_BEGINnamespace icu_78 {
171
172Locale *locale_set_default_internal(const char *id, UErrorCode& status) {
173 // Synchronize this entire function.
174 Mutex lock(&gDefaultLocaleMutex);
175
176 UBool canonicalize = false;
177
178 // If given a nullptr string for the locale id, grab the default
179 // name from the system.
180 // (Different from most other locale APIs, where a null name means use
181 // the current ICU default locale.)
182 if (id == nullptr) {
183 id = uprv_getDefaultLocaleID(); // This function not thread safe? TODO: verify.
184 canonicalize = true; // always canonicalize host ID
185 }
186
187 CharString localeNameBuf =
188 canonicalize ? ulocimp_canonicalize(id, status) : ulocimp_getName(id, status);
189
190 if (U_FAILURE(status)) {
191 return gDefaultLocale;
192 }
193
194 if (gDefaultLocalesHashT == nullptr) {
195 gDefaultLocalesHashT = uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status);
196 if (U_FAILURE(status)) {
197 return gDefaultLocale;
198 }
199 uhash_setValueDeleter(gDefaultLocalesHashT, deleteLocale);
200 ucln_common_registerCleanup(UCLN_COMMON_LOCALE, locale_cleanup);
201 }
202
203 Locale* newDefault = static_cast<Locale*>(uhash_get(gDefaultLocalesHashT, localeNameBuf.data()));
204 if (newDefault == nullptr) {
205 newDefault = new Locale(Locale::eBOGUS);
206 if (newDefault == nullptr) {
207 status = U_MEMORY_ALLOCATION_ERROR;
208 return gDefaultLocale;
209 }
210 newDefault->init(localeNameBuf.data(), false);
211 uhash_put(gDefaultLocalesHashT, const_cast<char*>(newDefault->getName()), newDefault, &status);
212 if (U_FAILURE(status)) {
213 return gDefaultLocale;
214 }
215 }
216 gDefaultLocale = newDefault;
217 return gDefaultLocale;
218}
219
220U_NAMESPACE_END}
221
222/* sfb 07/21/99 */
223U_CFUNCextern "C" void
224locale_set_default(const char *id)
225{
226 U_NAMESPACE_USEusing namespace icu_78;
227 UErrorCode status = U_ZERO_ERROR;
228 locale_set_default_internal(id, status);
229}
230/* end */
231
232U_CFUNCextern "C" const char *
233locale_get_default()
234{
235 U_NAMESPACE_USEusing namespace icu_78;
236 return Locale::getDefault().getName();
237}
238
239namespace {
240
241template <auto FIELD, typename T>
242void copyToArray(std::string_view sv, T* that) {
243 auto& field = that->*FIELD;
244 constexpr size_t capacity = std::extent_v<std::remove_reference_t<decltype(field)>>;
245 static_assert(capacity > 0);
246 if (!sv.empty()) {
247 U_ASSERT(sv.size() < capacity)(static_cast <bool> (sv.size() < capacity) ? void (0
) : __assert_fail ("sv.size() < capacity", __builtin_FILE (
), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
248 uprv_memcpy(field, sv.data(), sv.size())do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (field != __null) ? void (0) : __assert_fail
("field != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (sv.data() !=
__null) ? void (0) : __assert_fail ("sv.data() != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(field, sv.data(), sv.size()); } while
(false)
;
249 }
250 field[sv.size()] = '\0';
251}
252
253} // namespace
254
255U_NAMESPACE_BEGINnamespace icu_78 {
256
257void Locale::Nest::init(std::string_view language,
258 std::string_view script,
259 std::string_view region,
260 uint8_t variantBegin) {
261 copyToArray<&Nest::language>(language, this);
262 copyToArray<&Nest::script>(script, this);
263 copyToArray<&Nest::region>(region, this);
264 this->variantBegin = variantBegin;
265}
266
267Locale::Nest::Nest(Heap&& heap, uint8_t variantBegin) {
268 // When moving from Heap to Nest the language field can be left untouched
269 // (as it has the same offset in both) and only the script and region fields
270 // need to be copied to their new locations, which is safe to do because the
271 // new locations come before the old locations in memory and don't overlap.
272 static_assert(offsetof(Nest, region)__builtin_offsetof(Nest, region) <= offsetof(Heap, script)__builtin_offsetof(Heap, script));
273 static_assert(offsetof(Nest, variantBegin)__builtin_offsetof(Nest, variantBegin) <= offsetof(Heap, region)__builtin_offsetof(Heap, region));
274 U_ASSERT(this == reinterpret_cast<Nest*>(&heap))(static_cast <bool> (this == reinterpret_cast<Nest*>
(&heap)) ? void (0) : __assert_fail ("this == reinterpret_cast<Nest*>(&heap)"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
275 copyToArray<&Nest::script>(heap.script, this);
276 copyToArray<&Nest::region>(heap.region, this);
277 this->variantBegin = variantBegin;
278 *this->baseName = '\0';
279}
280
281struct Locale::Heap::Alloc : public UMemory {
282 FixedString fullName;
283 FixedString baseName;
284 int32_t variantBegin;
285
286 const char* getVariant() const { return variantBegin == 0 ? "" : getBaseName() + variantBegin; }
287 const char* getFullName() const { return fullName.data(); }
288 const char* getBaseName() const {
289 if (baseName.isEmpty()) {
290 if (const char* name = fullName.data(); *name != '@') {
291 return name;
292 }
293 }
294 return baseName.data();
295 }
296
297 Alloc(int32_t variantBegin) : fullName(), baseName(), variantBegin(variantBegin) {}
298
299 Alloc(const Alloc& other, UErrorCode& status)
300 : fullName(), baseName(), variantBegin(other.variantBegin) {
301 if (U_SUCCESS(status)) {
302 if (!other.fullName.isEmpty()) {
303 fullName = other.fullName;
304 if (fullName.isEmpty()) {
305 status = U_MEMORY_ALLOCATION_ERROR;
306 } else {
307 if (!other.baseName.isEmpty()) {
308 baseName = other.baseName;
309 if (baseName.isEmpty()) {
310 status = U_MEMORY_ALLOCATION_ERROR;
311 }
312 }
313 }
314 }
315 }
316 }
317
318 // Move should be done on the owner of the pointer to this object.
319 Alloc(Alloc&&) noexcept = delete;
320
321 ~Alloc() = default;
322};
323
324const char* Locale::Heap::getVariant() const { return ptr->getVariant(); }
325const char* Locale::Heap::getFullName() const { return ptr->getFullName(); }
326const char* Locale::Heap::getBaseName() const { return ptr->getBaseName(); }
327
328Locale::Heap::Heap(std::string_view language,
329 std::string_view script,
330 std::string_view region,
331 int32_t variantBegin) {
332 ptr = new Alloc(variantBegin);
333 if (ptr == nullptr) {
334 type = eBOGUS;
335 } else {
336 type = eHEAP;
337 copyToArray<&Heap::language>(language, this);
338 copyToArray<&Heap::script>(script, this);
339 copyToArray<&Heap::region>(region, this);
340 }
341}
342
343Locale::Heap::~Heap() {
344 U_ASSERT(type == eHEAP)(static_cast <bool> (type == eHEAP) ? void (0) : __assert_fail
("type == eHEAP", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
;
345 delete ptr;
346}
347
348Locale::Heap& Locale::Heap::operator=(const Heap& other) {
349 U_ASSERT(type == eBOGUS)(static_cast <bool> (type == eBOGUS) ? void (0) : __assert_fail
("type == eBOGUS", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
;
350 UErrorCode status = U_ZERO_ERROR;
351 ptr = new Alloc(*other.ptr, status);
352 if (ptr == nullptr || U_FAILURE(status)) {
353 delete ptr;
354 } else {
355 type = eHEAP;
356 uprv_memcpy(language, other.language, sizeof language)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (language != __null) ? void (0) :
__assert_fail ("language != __null", __builtin_FILE (), __builtin_LINE
(), __extension__ __PRETTY_FUNCTION__)); (static_cast <bool
> (other.language != __null) ? void (0) : __assert_fail ("other.language != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); clang diagnostic pop :: memcpy(language, other.language,
sizeof language); } while (false)
;
357 uprv_memcpy(script, other.script, sizeof script)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (script != __null) ? void (0) : __assert_fail
("script != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (other.script
!= __null) ? void (0) : __assert_fail ("other.script != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); clang diagnostic pop :: memcpy(script, other.script, sizeof
script); } while (false)
;
358 uprv_memcpy(region, other.region, sizeof region)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (region != __null) ? void (0) : __assert_fail
("region != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (other.region
!= __null) ? void (0) : __assert_fail ("other.region != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); clang diagnostic pop :: memcpy(region, other.region, sizeof
region); } while (false)
;
359 }
360 return *this;
361}
362
363Locale::Heap& Locale::Heap::operator=(Heap&& other) noexcept {
364 U_ASSERT(type == eBOGUS)(static_cast <bool> (type == eBOGUS) ? void (0) : __assert_fail
("type == eBOGUS", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
;
365 ptr = other.ptr;
366 type = eHEAP;
367 other.type = eBOGUS;
368 uprv_memcpy(language, other.language, sizeof language)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (language != __null) ? void (0) :
__assert_fail ("language != __null", __builtin_FILE (), __builtin_LINE
(), __extension__ __PRETTY_FUNCTION__)); (static_cast <bool
> (other.language != __null) ? void (0) : __assert_fail ("other.language != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); clang diagnostic pop :: memcpy(language, other.language,
sizeof language); } while (false)
;
369 uprv_memcpy(script, other.script, sizeof script)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (script != __null) ? void (0) : __assert_fail
("script != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (other.script
!= __null) ? void (0) : __assert_fail ("other.script != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); clang diagnostic pop :: memcpy(script, other.script, sizeof
script); } while (false)
;
370 uprv_memcpy(region, other.region, sizeof region)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (region != __null) ? void (0) : __assert_fail
("region != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (other.region
!= __null) ? void (0) : __assert_fail ("other.region != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); clang diagnostic pop :: memcpy(region, other.region, sizeof
region); } while (false)
;
371 return *this;
372}
373
374template <typename BogusFn, typename NestFn, typename HeapFn, typename... Args>
375auto Locale::Payload::visit(BogusFn bogusFn, NestFn nestFn, HeapFn heapFn, Args... args) const {
376 switch (type) {
377 case eBOGUS:
378 return bogusFn(args...);
379 case eNEST:
380 return nestFn(nest, args...);
381 case eHEAP:
382 return heapFn(heap, args...);
383 default:
384 UPRV_UNREACHABLE_EXITabort();
385 };
386}
387
388void Locale::Payload::copy(const Payload& other) {
389 other.visit([](Payload*) {},
390 [](const Nest& nest, Payload* dst) { dst->nest = nest; },
391 [](const Heap& heap, Payload* dst) { dst->heap = heap; },
392 this);
393}
394
395void Locale::Payload::move(Payload&& other) noexcept {
396 other.visit(
397 [](Payload*) {},
398 [](const Nest& nest, Payload* dst) { dst->nest = nest; },
399 [](const Heap& heap, Payload* dst) { dst->heap = std::move(const_cast<Heap&>(heap)); },
400 this);
401}
402
403Locale::Payload::~Payload() {
404 if (type == eHEAP) { heap.~Heap(); }
405}
406
407Locale::Payload::Payload(const Payload& other) : type{eBOGUS} { copy(other); }
408Locale::Payload::Payload(Payload&& other) noexcept : type{eBOGUS} { move(std::move(other)); }
409
410Locale::Payload& Locale::Payload::operator=(const Payload& other) {
411 if (this != &other) {
412 setToBogus();
413 copy(other);
414 }
415 return *this;
416}
417
418Locale::Payload& Locale::Payload::operator=(Payload&& other) noexcept {
419 if (this != &other) {
420 setToBogus();
421 move(std::move(other));
422 }
423 return *this;
424}
425
426void Locale::Payload::setToBogus() {
427 this->~Payload();
428 type = eBOGUS;
429}
430
431template <typename T, typename... Args> T& Locale::Payload::emplace(Args&&... args) {
432 if constexpr (std::is_same_v<T, Nest>) {
433 this->~Payload();
434 ::new (&nest) Nest(std::forward<Args>(args)...);
435 return nest;
436 }
437 if constexpr (std::is_same_v<T, Heap>) {
438 U_ASSERT(type != eHEAP)(static_cast <bool> (type != eHEAP) ? void (0) : __assert_fail
("type != eHEAP", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
;
439 ::new (&heap) Heap(std::forward<Args>(args)...);
440 return heap;
441 }
442}
443
444template <> Locale::Nest* Locale::Payload::get() { return type == eNEST ? &nest : nullptr; }
445template <> Locale::Heap* Locale::Payload::get() { return type == eHEAP ? &heap : nullptr; }
446
447UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Locale)UClassID Locale::getStaticClassID() { static char classID = 0
; return (UClassID)&classID; } UClassID Locale::getDynamicClassID
() const { return Locale::getStaticClassID(); }
448
449/*Character separating the posix id fields*/
450// '_'
451// In the platform codepage.
452#define SEP_CHAR'_' '_'
453#define NULL_CHAR'\0' '\0'
454
455Locale::~Locale() = default;
456
457Locale::Locale()
458 : UObject(), payload()
459{
460 init(nullptr, false);
461}
462
463/*
464 * Internal constructor to allow construction of a locale object with
465 * NO side effects. (Default constructor tries to get
466 * the default locale.)
467 */
468Locale::Locale(Locale::ELocaleType)
469 : UObject(), payload()
470{
471}
472
473
474Locale::Locale( const char * newLanguage,
475 const char * newCountry,
476 const char * newVariant,
477 const char * newKeywords)
478 : UObject(), payload()
479{
480 if( (newLanguage==nullptr) && (newCountry == nullptr) && (newVariant == nullptr) )
481 {
482 init(nullptr, false); /* shortcut */
483 }
484 else
485 {
486 UErrorCode status = U_ZERO_ERROR;
487 int32_t lsize = 0;
488 int32_t csize = 0;
489 int32_t vsize = 0;
490 int32_t ksize = 0;
491
492 // Check the sizes of the input strings.
493
494 // Language
495 if ( newLanguage != nullptr )
496 {
497 lsize = static_cast<int32_t>(uprv_strlen(newLanguage):: strlen(newLanguage));
498 if ( lsize < 0 || lsize > ULOC_STRING_LIMIT357913941 ) { // int32 wrap
499 return;
500 }
501 }
502
503 CharString togo(newLanguage, lsize, status); // start with newLanguage
504
505 // _Country
506 if ( newCountry != nullptr )
507 {
508 csize = static_cast<int32_t>(uprv_strlen(newCountry):: strlen(newCountry));
509 if ( csize < 0 || csize > ULOC_STRING_LIMIT357913941 ) { // int32 wrap
510 return;
511 }
512 }
513
514 // _Variant
515 if ( newVariant != nullptr )
516 {
517 // remove leading _'s
518 while(newVariant[0] == SEP_CHAR'_')
519 {
520 newVariant++;
521 }
522
523 // remove trailing _'s
524 vsize = static_cast<int32_t>(uprv_strlen(newVariant):: strlen(newVariant));
525 if ( vsize < 0 || vsize > ULOC_STRING_LIMIT357913941 ) { // int32 wrap
526 return;
527 }
528 while( (vsize>1) && (newVariant[vsize-1] == SEP_CHAR'_') )
529 {
530 vsize--;
531 }
532 }
533
534 if ( newKeywords != nullptr)
535 {
536 ksize = static_cast<int32_t>(uprv_strlen(newKeywords):: strlen(newKeywords));
537 if ( ksize < 0 || ksize > ULOC_STRING_LIMIT357913941 ) {
538 return;
539 }
540 }
541
542 // We've checked the input sizes, now build up the full locale string..
543
544 // newLanguage is already copied
545
546 if ( ( vsize != 0 ) || (csize != 0) ) // at least: __v
547 { // ^
548 togo.append(SEP_CHAR'_', status);
549 }
550
551 if ( csize != 0 )
552 {
553 togo.append(newCountry, status);
554 }
555
556 if ( vsize != 0)
557 {
558 togo.append(SEP_CHAR'_', status)
559 .append(newVariant, vsize, status);
560 }
561
562 if ( ksize != 0)
563 {
564 if (uprv_strchr(newKeywords, '='):: strchr(newKeywords, '=')) {
565 togo.append('@', status); /* keyword parsing */
566 }
567 else {
568 togo.append('_', status); /* Variant parsing with a script */
569 if ( vsize == 0) {
570 togo.append('_', status); /* No country found */
571 }
572 }
573 togo.append(newKeywords, status);
574 }
575
576 if (U_FAILURE(status)) {
577 // Something went wrong with appending, etc.
578 return;
579 }
580 // Parse it, because for example 'language' might really be a complete
581 // string.
582 init(togo.data(), false);
583 }
584}
585
586Locale::Locale(const Locale&) = default;
587Locale::Locale(Locale&&) noexcept = default;
588
589Locale& Locale::operator=(const Locale&) = default;
590Locale& Locale::operator=(Locale&&) noexcept = default;
591
592Locale *
593Locale::clone() const {
594 return new Locale(*this);
595}
596
597bool
598Locale::operator==( const Locale& other) const
599{
600 return uprv_strcmp(other.getName(), getName()):: strcmp(other.getName(), getName()) == 0;
601}
602
603namespace {
604
605UInitOnce gKnownCanonicalizedInitOnce {};
606UHashtable *gKnownCanonicalized = nullptr;
607
608constexpr const char* KNOWN_CANONICALIZED[] = {
609 "c",
610 // Commonly used locales known are already canonicalized
611 "af", "af_ZA", "am", "am_ET", "ar", "ar_001", "as", "as_IN", "az", "az_AZ",
612 "be", "be_BY", "bg", "bg_BG", "bn", "bn_IN", "bs", "bs_BA", "ca", "ca_ES",
613 "cs", "cs_CZ", "cy", "cy_GB", "da", "da_DK", "de", "de_DE", "el", "el_GR",
614 "en", "en_GB", "en_US", "es", "es_419", "es_ES", "et", "et_EE", "eu",
615 "eu_ES", "fa", "fa_IR", "fi", "fi_FI", "fil", "fil_PH", "fr", "fr_FR",
616 "ga", "ga_IE", "gl", "gl_ES", "gu", "gu_IN", "he", "he_IL", "hi", "hi_IN",
617 "hr", "hr_HR", "hu", "hu_HU", "hy", "hy_AM", "id", "id_ID", "is", "is_IS",
618 "it", "it_IT", "ja", "ja_JP", "jv", "jv_ID", "ka", "ka_GE", "kk", "kk_KZ",
619 "km", "km_KH", "kn", "kn_IN", "ko", "ko_KR", "ky", "ky_KG", "lo", "lo_LA",
620 "lt", "lt_LT", "lv", "lv_LV", "mk", "mk_MK", "ml", "ml_IN", "mn", "mn_MN",
621 "mr", "mr_IN", "ms", "ms_MY", "my", "my_MM", "nb", "nb_NO", "ne", "ne_NP",
622 "nl", "nl_NL", "no", "or", "or_IN", "pa", "pa_IN", "pl", "pl_PL", "ps", "ps_AF",
623 "pt", "pt_BR", "pt_PT", "ro", "ro_RO", "ru", "ru_RU", "sd", "sd_IN", "si",
624 "si_LK", "sk", "sk_SK", "sl", "sl_SI", "so", "so_SO", "sq", "sq_AL", "sr",
625 "sr_Cyrl_RS", "sr_Latn", "sr_RS", "sv", "sv_SE", "sw", "sw_TZ", "ta",
626 "ta_IN", "te", "te_IN", "th", "th_TH", "tk", "tk_TM", "tr", "tr_TR", "uk",
627 "uk_UA", "ur", "ur_PK", "uz", "uz_UZ", "vi", "vi_VN", "yue", "yue_Hant",
628 "yue_Hant_HK", "yue_HK", "zh", "zh_CN", "zh_Hans", "zh_Hans_CN", "zh_Hant",
629 "zh_Hant_TW", "zh_TW", "zu", "zu_ZA"
630};
631
632UBool U_CALLCONV cleanupKnownCanonicalized() {
633 gKnownCanonicalizedInitOnce.reset();
634 if (gKnownCanonicalized) { uhash_close(gKnownCanonicalized); }
635 return true;
636}
637
638void U_CALLCONV loadKnownCanonicalized(UErrorCode &status) {
639 ucln_common_registerCleanup(UCLN_COMMON_LOCALE_KNOWN_CANONICALIZED,
640 cleanupKnownCanonicalized);
641 LocalUHashtablePointer newKnownCanonicalizedMap(
642 uhash_open(uhash_hashChars, uhash_compareChars, nullptr, &status));
643 for (int32_t i = 0;
644 U_SUCCESS(status) && i < UPRV_LENGTHOF(KNOWN_CANONICALIZED)(int32_t)(sizeof(KNOWN_CANONICALIZED)/sizeof((KNOWN_CANONICALIZED
)[0]))
;
645 i++) {
646 uhash_puti(newKnownCanonicalizedMap.getAlias(),
647 (void*)KNOWN_CANONICALIZED[i],
648 1, &status);
649 }
650 if (U_FAILURE(status)) {
651 return;
652 }
653
654 gKnownCanonicalized = newKnownCanonicalizedMap.orphan();
655}
656
657class AliasData;
658
659/**
660 * A Builder class to build the alias data.
661 */
662class AliasDataBuilder {
663public:
664 AliasDataBuilder() {
665 }
666
667 // Build the AliasData from resource.
668 AliasData* build(UErrorCode &status);
669
670private:
671 void readAlias(UResourceBundle* alias,
672 UniqueCharStrings* strings,
673 LocalMemory<const char*>& types,
674 LocalMemory<int32_t>& replacementIndexes,
675 int32_t &length,
676 void (*checkType)(const char* type),
677 void (*checkReplacement)(const UChar* replacement),
678 UErrorCode &status);
679
680 // Read the languageAlias data from alias to
681 // strings+types+replacementIndexes
682 // The number of record will be stored into length.
683 // Allocate length items for types, to store the type field.
684 // Allocate length items for replacementIndexes,
685 // to store the index in the strings for the replacement script.
686 void readLanguageAlias(UResourceBundle* alias,
687 UniqueCharStrings* strings,
688 LocalMemory<const char*>& types,
689 LocalMemory<int32_t>& replacementIndexes,
690 int32_t &length,
691 UErrorCode &status);
692
693 // Read the scriptAlias data from alias to
694 // strings+types+replacementIndexes
695 // Allocate length items for types, to store the type field.
696 // Allocate length items for replacementIndexes,
697 // to store the index in the strings for the replacement script.
698 void readScriptAlias(UResourceBundle* alias,
699 UniqueCharStrings* strings,
700 LocalMemory<const char*>& types,
701 LocalMemory<int32_t>& replacementIndexes,
702 int32_t &length, UErrorCode &status);
703
704 // Read the territoryAlias data from alias to
705 // strings+types+replacementIndexes
706 // Allocate length items for types, to store the type field.
707 // Allocate length items for replacementIndexes,
708 // to store the index in the strings for the replacement script.
709 void readTerritoryAlias(UResourceBundle* alias,
710 UniqueCharStrings* strings,
711 LocalMemory<const char*>& types,
712 LocalMemory<int32_t>& replacementIndexes,
713 int32_t &length, UErrorCode &status);
714
715 // Read the variantAlias data from alias to
716 // strings+types+replacementIndexes
717 // Allocate length items for types, to store the type field.
718 // Allocate length items for replacementIndexes,
719 // to store the index in the strings for the replacement variant.
720 void readVariantAlias(UResourceBundle* alias,
721 UniqueCharStrings* strings,
722 LocalMemory<const char*>& types,
723 LocalMemory<int32_t>& replacementIndexes,
724 int32_t &length, UErrorCode &status);
725
726 // Read the subdivisionAlias data from alias to
727 // strings+types+replacementIndexes
728 // Allocate length items for types, to store the type field.
729 // Allocate length items for replacementIndexes,
730 // to store the index in the strings for the replacement variant.
731 void readSubdivisionAlias(UResourceBundle* alias,
732 UniqueCharStrings* strings,
733 LocalMemory<const char*>& types,
734 LocalMemory<int32_t>& replacementIndexes,
735 int32_t &length, UErrorCode &status);
736};
737
738/**
739 * A class to hold the Alias Data.
740 */
741class AliasData : public UMemory {
742public:
743 static const AliasData* singleton(UErrorCode& status) {
744 if (U_FAILURE(status)) {
745 // Do not get into loadData if the status already has error.
746 return nullptr;
747 }
748 umtx_initOnce(AliasData::gInitOnce, &AliasData::loadData, status);
749 return gSingleton;
750 }
751
752 const CharStringMap& languageMap() const { return language; }
753 const CharStringMap& scriptMap() const { return script; }
754 const CharStringMap& territoryMap() const { return territory; }
755 const CharStringMap& variantMap() const { return variant; }
756 const CharStringMap& subdivisionMap() const { return subdivision; }
757
758 static void U_CALLCONV loadData(UErrorCode &status);
759 static UBool U_CALLCONV cleanup();
760
761 static UInitOnce gInitOnce;
762
763private:
764 AliasData(CharStringMap languageMap,
765 CharStringMap scriptMap,
766 CharStringMap territoryMap,
767 CharStringMap variantMap,
768 CharStringMap subdivisionMap,
769 CharString* strings)
770 : language(std::move(languageMap)),
771 script(std::move(scriptMap)),
772 territory(std::move(territoryMap)),
773 variant(std::move(variantMap)),
774 subdivision(std::move(subdivisionMap)),
775 strings(strings) {
776 }
777
778 ~AliasData() {
779 delete strings;
780 }
781
782 static const AliasData* gSingleton;
783
784 CharStringMap language;
785 CharStringMap script;
786 CharStringMap territory;
787 CharStringMap variant;
788 CharStringMap subdivision;
789 CharString* strings;
790
791 friend class AliasDataBuilder;
792};
793
794
795const AliasData* AliasData::gSingleton = nullptr;
796UInitOnce AliasData::gInitOnce {};
797
798UBool U_CALLCONV
799AliasData::cleanup()
800{
801 gInitOnce.reset();
802 delete gSingleton;
803 return true;
804}
805
806void
807AliasDataBuilder::readAlias(
808 UResourceBundle* alias,
809 UniqueCharStrings* strings,
810 LocalMemory<const char*>& types,
811 LocalMemory<int32_t>& replacementIndexes,
812 int32_t &length,
813 void (*checkType)(const char* type),
814 void (*checkReplacement)(const UChar* replacement),
815 UErrorCode &status) {
816 if (U_FAILURE(status)) {
12
Taking false branch
817 return;
818 }
819 length = ures_getSize(alias);
820 const char** rawTypes = types.allocateInsteadAndCopy(length);
821 if (rawTypes == nullptr) {
13
Taking true branch
822 status = U_MEMORY_ALLOCATION_ERROR;
823 return;
14
Returning without writing to 'types.ptr'
824 }
825 int32_t* rawIndexes = replacementIndexes.allocateInsteadAndCopy(length);
826 if (rawIndexes == nullptr) {
827 status = U_MEMORY_ALLOCATION_ERROR;
828 return;
829 }
830 for (int i = 0; U_SUCCESS(status) && ures_hasNext(alias); i++) {
831 LocalUResourceBundlePointer res(
832 ures_getNextResource(alias, nullptr, &status));
833 const char* aliasFrom = ures_getKey(res.getAlias());
834 const UChar* aliasTo =
835 ures_getStringByKey(res.getAlias(), "replacement", nullptr, &status);
836 if (U_FAILURE(status)) return;
837
838 checkType(aliasFrom);
839 checkReplacement(aliasTo);
840
841 rawTypes[i] = aliasFrom;
842 rawIndexes[i] = strings->add(aliasTo, status);
843 }
844}
845
846/**
847 * Read the languageAlias data from alias to strings+types+replacementIndexes.
848 * Allocate length items for types, to store the type field. Allocate length
849 * items for replacementIndexes, to store the index in the strings for the
850 * replacement language.
851 */
852void
853AliasDataBuilder::readLanguageAlias(
854 UResourceBundle* alias,
855 UniqueCharStrings* strings,
856 LocalMemory<const char*>& types,
857 LocalMemory<int32_t>& replacementIndexes,
858 int32_t &length,
859 UErrorCode &status)
860{
861 return readAlias(
11
Calling 'AliasDataBuilder::readAlias'
15
Returning from 'AliasDataBuilder::readAlias'
16
Returning without writing to 'types.ptr'
862 alias, strings, types, replacementIndexes, length,
863#if U_DEBUG1
864 [](const char* type) {
865 // Assert the aliasFrom only contains the following possibilities
866 // language_REGION_variant
867 // language_REGION
868 // language_variant
869 // language
870 // und_variant
871 Locale test(type);
872 // Assert no script in aliasFrom
873 U_ASSERT(test.getScript()[0] == '\0')(static_cast <bool> (test.getScript()[0] == '\0') ? void
(0) : __assert_fail ("test.getScript()[0] == '\\0'", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
874 // Assert when language is und, no REGION in aliasFrom.
875 U_ASSERT(test.getLanguage()[0] != '\0' || test.getCountry()[0] == '\0')(static_cast <bool> (test.getLanguage()[0] != '\0' || test
.getCountry()[0] == '\0') ? void (0) : __assert_fail ("test.getLanguage()[0] != '\\0' || test.getCountry()[0] == '\\0'"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
876 },
877#else
878 [](const char*) {},
879#endif
880 [](const UChar*) {}, status);
881}
882
883/**
884 * Read the scriptAlias data from alias to strings+types+replacementIndexes.
885 * Allocate length items for types, to store the type field. Allocate length
886 * items for replacementIndexes, to store the index in the strings for the
887 * replacement script.
888 */
889void
890AliasDataBuilder::readScriptAlias(
891 UResourceBundle* alias,
892 UniqueCharStrings* strings,
893 LocalMemory<const char*>& types,
894 LocalMemory<int32_t>& replacementIndexes,
895 int32_t &length,
896 UErrorCode &status)
897{
898 return readAlias(
899 alias, strings, types, replacementIndexes, length,
900#if U_DEBUG1
901 [](const char* type) {
902 U_ASSERT(uprv_strlen(type) == 4)(static_cast <bool> (:: strlen(type) == 4) ? void (0) :
__assert_fail (":: strlen(type) == 4", __builtin_FILE (), __builtin_LINE
(), __extension__ __PRETTY_FUNCTION__))
;
903 },
904 [](const UChar* replacement) {
905 U_ASSERT(u_strlen(replacement) == 4)(static_cast <bool> (u_strlen(replacement) == 4) ? void
(0) : __assert_fail ("u_strlen(replacement) == 4", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
906 },
907#else
908 [](const char*) {},
909 [](const UChar*) { },
910#endif
911 status);
912}
913
914/**
915 * Read the territoryAlias data from alias to strings+types+replacementIndexes.
916 * Allocate length items for types, to store the type field. Allocate length
917 * items for replacementIndexes, to store the index in the strings for the
918 * replacement regions.
919 */
920void
921AliasDataBuilder::readTerritoryAlias(
922 UResourceBundle* alias,
923 UniqueCharStrings* strings,
924 LocalMemory<const char*>& types,
925 LocalMemory<int32_t>& replacementIndexes,
926 int32_t &length,
927 UErrorCode &status)
928{
929 return readAlias(
930 alias, strings, types, replacementIndexes, length,
931#if U_DEBUG1
932 [](const char* type) {
933 U_ASSERT(uprv_strlen(type) == 2 || uprv_strlen(type) == 3)(static_cast <bool> (:: strlen(type) == 2 || :: strlen(
type) == 3) ? void (0) : __assert_fail (":: strlen(type) == 2 || :: strlen(type) == 3"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
934 },
935#else
936 [](const char*) {},
937#endif
938 [](const UChar*) { },
939 status);
940}
941
942/**
943 * Read the variantAlias data from alias to strings+types+replacementIndexes.
944 * Allocate length items for types, to store the type field. Allocate length
945 * items for replacementIndexes, to store the index in the strings for the
946 * replacement variant.
947 */
948void
949AliasDataBuilder::readVariantAlias(
950 UResourceBundle* alias,
951 UniqueCharStrings* strings,
952 LocalMemory<const char*>& types,
953 LocalMemory<int32_t>& replacementIndexes,
954 int32_t &length,
955 UErrorCode &status)
956{
957 return readAlias(
958 alias, strings, types, replacementIndexes, length,
959#if U_DEBUG1
960 [](const char* type) {
961 U_ASSERT(uprv_strlen(type) >= 4 && uprv_strlen(type) <= 8)(static_cast <bool> (:: strlen(type) >= 4 &&
:: strlen(type) <= 8) ? void (0) : __assert_fail (":: strlen(type) >= 4 && :: strlen(type) <= 8"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
962 U_ASSERT(uprv_strlen(type) != 4 ||(static_cast <bool> (:: strlen(type) != 4 || (type[0] >=
'0' && type[0] <= '9')) ? void (0) : __assert_fail
(":: strlen(type) != 4 || (type[0] >= '0' && type[0] <= '9')"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
963 (type[0] >= '0' && type[0] <= '9'))(static_cast <bool> (:: strlen(type) != 4 || (type[0] >=
'0' && type[0] <= '9')) ? void (0) : __assert_fail
(":: strlen(type) != 4 || (type[0] >= '0' && type[0] <= '9')"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
964 },
965 [](const UChar* replacement) {
966 int32_t len = u_strlen(replacement);
967 U_ASSERT(len >= 4 && len <= 8)(static_cast <bool> (len >= 4 && len <= 8
) ? void (0) : __assert_fail ("len >= 4 && len <= 8"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
968 U_ASSERT(len != 4 ||(static_cast <bool> (len != 4 || (*replacement >= u'0'
&& *replacement <= u'9')) ? void (0) : __assert_fail
("len != 4 || (*replacement >= u'0' && *replacement <= u'9')"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
969 (*replacement >= u'0' &&(static_cast <bool> (len != 4 || (*replacement >= u'0'
&& *replacement <= u'9')) ? void (0) : __assert_fail
("len != 4 || (*replacement >= u'0' && *replacement <= u'9')"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
970 *replacement <= u'9'))(static_cast <bool> (len != 4 || (*replacement >= u'0'
&& *replacement <= u'9')) ? void (0) : __assert_fail
("len != 4 || (*replacement >= u'0' && *replacement <= u'9')"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
971 },
972#else
973 [](const char*) {},
974 [](const UChar*) { },
975#endif
976 status);
977}
978
979/**
980 * Read the subdivisionAlias data from alias to strings+types+replacementIndexes.
981 * Allocate length items for types, to store the type field. Allocate length
982 * items for replacementIndexes, to store the index in the strings for the
983 * replacement regions.
984 */
985void
986AliasDataBuilder::readSubdivisionAlias(
987 UResourceBundle* alias,
988 UniqueCharStrings* strings,
989 LocalMemory<const char*>& types,
990 LocalMemory<int32_t>& replacementIndexes,
991 int32_t &length,
992 UErrorCode &status)
993{
994 return readAlias(
995 alias, strings, types, replacementIndexes, length,
996#if U_DEBUG1
997 [](const char* type) {
998 U_ASSERT(uprv_strlen(type) >= 3 && uprv_strlen(type) <= 8)(static_cast <bool> (:: strlen(type) >= 3 &&
:: strlen(type) <= 8) ? void (0) : __assert_fail (":: strlen(type) >= 3 && :: strlen(type) <= 8"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
999 },
1000#else
1001 [](const char*) {},
1002#endif
1003 [](const UChar*) { },
1004 status);
1005}
1006
1007/**
1008 * Initializes the alias data from the ICU resource bundles. The alias data
1009 * contains alias of language, country, script and variants.
1010 *
1011 * If the alias data has already loaded, then this method simply returns without
1012 * doing anything meaningful.
1013 */
1014void U_CALLCONV
1015AliasData::loadData(UErrorCode &status)
1016{
1017#ifdef LOCALE_CANONICALIZATION_DEBUG
1018 UDate start = uprv_getRawUTCtime();
1019#endif // LOCALE_CANONICALIZATION_DEBUG
1020 ucln_common_registerCleanup(UCLN_COMMON_LOCALE_ALIAS, cleanup);
1021 AliasDataBuilder builder;
1022 gSingleton = builder.build(status);
1023#ifdef LOCALE_CANONICALIZATION_DEBUG
1024 UDate end = uprv_getRawUTCtime();
1025 printf("AliasData::loadData took total %f ms\n", end - start);
1026#endif // LOCALE_CANONICALIZATION_DEBUG
1027}
1028
1029/**
1030 * Build the alias data from resources.
1031 */
1032AliasData*
1033AliasDataBuilder::build(UErrorCode &status) {
1034 if (U_FAILURE(status)) { return nullptr; }
1
Taking false branch
1035
1036 LocalUResourceBundlePointer metadata(
1037 ures_openDirect(nullptr, "metadata", &status));
1038 LocalUResourceBundlePointer metadataAlias(
1039 ures_getByKey(metadata.getAlias(), "alias", nullptr, &status));
1040 LocalUResourceBundlePointer languageAlias(
1041 ures_getByKey(metadataAlias.getAlias(), "language", nullptr, &status));
1042 LocalUResourceBundlePointer scriptAlias(
1043 ures_getByKey(metadataAlias.getAlias(), "script", nullptr, &status));
1044 LocalUResourceBundlePointer territoryAlias(
1045 ures_getByKey(metadataAlias.getAlias(), "territory", nullptr, &status));
1046 LocalUResourceBundlePointer variantAlias(
1047 ures_getByKey(metadataAlias.getAlias(), "variant", nullptr, &status));
1048 LocalUResourceBundlePointer subdivisionAlias(
1049 ures_getByKey(metadataAlias.getAlias(), "subdivision", nullptr, &status));
1050
1051 if (U_FAILURE(status)) {
2
Taking false branch
1052 return nullptr;
1053 }
1054 int32_t languagesLength = 0, scriptLength = 0, territoryLength = 0,
1055 variantLength = 0, subdivisionLength = 0;
1056
1057 // Read the languageAlias into languageTypes, languageReplacementIndexes
1058 // and strings
1059 UniqueCharStrings strings(status);
1060 LocalMemory<const char*> languageTypes;
3
Passing null pointer value via 1st parameter 'p'
4
Calling default constructor for 'LocalMemory<const char *>'
9
Returning from default constructor for 'LocalMemory<const char *>'
1061 LocalMemory<int32_t> languageReplacementIndexes;
1062 readLanguageAlias(languageAlias.getAlias(),
10
Calling 'AliasDataBuilder::readLanguageAlias'
17
Returning from 'AliasDataBuilder::readLanguageAlias'
1063 &strings,
1064 languageTypes,
1065 languageReplacementIndexes,
1066 languagesLength,
1067 status);
1068
1069 // Read the scriptAlias into scriptTypes, scriptReplacementIndexes
1070 // and strings
1071 LocalMemory<const char*> scriptTypes;
1072 LocalMemory<int32_t> scriptReplacementIndexes;
1073 readScriptAlias(scriptAlias.getAlias(),
1074 &strings,
1075 scriptTypes,
1076 scriptReplacementIndexes,
1077 scriptLength,
1078 status);
1079
1080 // Read the territoryAlias into territoryTypes, territoryReplacementIndexes
1081 // and strings
1082 LocalMemory<const char*> territoryTypes;
1083 LocalMemory<int32_t> territoryReplacementIndexes;
1084 readTerritoryAlias(territoryAlias.getAlias(),
1085 &strings,
1086 territoryTypes,
1087 territoryReplacementIndexes,
1088 territoryLength, status);
1089
1090 // Read the variantAlias into variantTypes, variantReplacementIndexes
1091 // and strings
1092 LocalMemory<const char*> variantTypes;
1093 LocalMemory<int32_t> variantReplacementIndexes;
1094 readVariantAlias(variantAlias.getAlias(),
1095 &strings,
1096 variantTypes,
1097 variantReplacementIndexes,
1098 variantLength, status);
1099
1100 // Read the subdivisionAlias into subdivisionTypes, subdivisionReplacementIndexes
1101 // and strings
1102 LocalMemory<const char*> subdivisionTypes;
1103 LocalMemory<int32_t> subdivisionReplacementIndexes;
1104 readSubdivisionAlias(subdivisionAlias.getAlias(),
1105 &strings,
1106 subdivisionTypes,
1107 subdivisionReplacementIndexes,
1108 subdivisionLength, status);
1109
1110 if (U_FAILURE(status)) {
18
Taking false branch
1111 return nullptr;
1112 }
1113
1114 // We can only use strings after freeze it.
1115 strings.freeze();
1116
1117 // Build the languageMap from languageTypes & languageReplacementIndexes
1118 CharStringMap languageMap(490, status);
1119 for (int32_t i = 0; U_SUCCESS(status) && i
18.1
'i' is < 'languagesLength'
18.1
'i' is < 'languagesLength'
18.1
'i' is < 'languagesLength'
< languagesLength; i++) {
19
Loop condition is true. Entering loop body
1120 languageMap.put(languageTypes[i],
20
Calling 'LocalMemory::operator[]'
1121 strings.get(languageReplacementIndexes[i]),
1122 status);
1123 }
1124
1125 // Build the scriptMap from scriptTypes & scriptReplacementIndexes
1126 CharStringMap scriptMap(1, status);
1127 for (int32_t i = 0; U_SUCCESS(status) && i < scriptLength; i++) {
1128 scriptMap.put(scriptTypes[i],
1129 strings.get(scriptReplacementIndexes[i]),
1130 status);
1131 }
1132
1133 // Build the territoryMap from territoryTypes & territoryReplacementIndexes
1134 CharStringMap territoryMap(650, status);
1135 for (int32_t i = 0; U_SUCCESS(status) && i < territoryLength; i++) {
1136 territoryMap.put(territoryTypes[i],
1137 strings.get(territoryReplacementIndexes[i]),
1138 status);
1139 }
1140
1141 // Build the variantMap from variantTypes & variantReplacementIndexes.
1142 CharStringMap variantMap(2, status);
1143 for (int32_t i = 0; U_SUCCESS(status) && i < variantLength; i++) {
1144 variantMap.put(variantTypes[i],
1145 strings.get(variantReplacementIndexes[i]),
1146 status);
1147 }
1148
1149 // Build the subdivisionMap from subdivisionTypes & subdivisionReplacementIndexes.
1150 CharStringMap subdivisionMap(2, status);
1151 for (int32_t i = 0; U_SUCCESS(status) && i < subdivisionLength; i++) {
1152 subdivisionMap.put(subdivisionTypes[i],
1153 strings.get(subdivisionReplacementIndexes[i]),
1154 status);
1155 }
1156
1157 if (U_FAILURE(status)) {
1158 return nullptr;
1159 }
1160
1161 // copy hashtables
1162 auto *data = new AliasData(
1163 std::move(languageMap),
1164 std::move(scriptMap),
1165 std::move(territoryMap),
1166 std::move(variantMap),
1167 std::move(subdivisionMap),
1168 strings.orphanCharStrings());
1169
1170 if (data == nullptr) {
1171 status = U_MEMORY_ALLOCATION_ERROR;
1172 }
1173 return data;
1174}
1175
1176/**
1177 * A class that find the replacement values of locale fields by using AliasData.
1178 */
1179class AliasReplacer {
1180public:
1181 AliasReplacer(UErrorCode& status) :
1182 language(nullptr), script(nullptr), region(nullptr),
1183 extensions(nullptr),
1184 // store value in variants only once
1185 variants(nullptr,
1186 ([](UElement e1, UElement e2) -> UBool {
1187 return 0==uprv_strcmp((const char*)e1.pointer,:: strcmp((const char*)e1.pointer, (const char*)e2.pointer)
1188 (const char*)e2.pointer):: strcmp((const char*)e1.pointer, (const char*)e2.pointer);}),
1189 status),
1190 data(nullptr) {
1191 }
1192 ~AliasReplacer() {
1193 }
1194
1195 // Check the fields inside locale, if need to replace fields,
1196 // place the replaced locale ID in out and return true.
1197 // Otherwise return false for no replacement or error.
1198 bool replace(
1199 const Locale& locale, CharString& out, UErrorCode& status);
1200
1201private:
1202 const char* language;
1203 const char* script;
1204 const char* region;
1205 const char* extensions;
1206 UVector variants;
1207
1208 const AliasData* data;
1209
1210 inline bool notEmpty(const char* str) {
1211 return str && str[0] != NULL_CHAR'\0';
1212 }
1213
1214 /**
1215 * If replacement is neither null nor empty and input is either null or empty,
1216 * return replacement.
1217 * If replacement is neither null nor empty but input is not empty, return input.
1218 * If replacement is either null or empty and type is either null or empty,
1219 * return input.
1220 * Otherwise return null.
1221 * replacement input type return
1222 * AAA nullptr * AAA
1223 * AAA BBB * BBB
1224 * nullptr || "" CCC nullptr CCC
1225 * nullptr || "" * DDD nullptr
1226 */
1227 inline const char* deleteOrReplace(
1228 const char* input, const char* type, const char* replacement) {
1229 return notEmpty(replacement) ?
1230 ((input == nullptr) ? replacement : input) :
1231 ((type == nullptr) ? input : nullptr);
1232 }
1233
1234 inline bool same(const char* a, const char* b) {
1235 if (a == nullptr && b == nullptr) {
1236 return true;
1237 }
1238 if ((a == nullptr && b != nullptr) ||
1239 (a != nullptr && b == nullptr)) {
1240 return false;
1241 }
1242 return uprv_strcmp(a, b):: strcmp(a, b) == 0;
1243 }
1244
1245 // Gather fields and generate locale ID into out.
1246 CharString& outputToString(CharString& out, UErrorCode& status);
1247
1248 // Generate the lookup key.
1249 CharString& generateKey(const char* language, const char* region,
1250 const char* variant, CharString& out,
1251 UErrorCode& status);
1252
1253 void parseLanguageReplacement(const char* replacement,
1254 const char*& replaceLanguage,
1255 const char*& replaceScript,
1256 const char*& replaceRegion,
1257 const char*& replaceVariant,
1258 const char*& replaceExtensions,
1259 UVector& toBeFreed,
1260 UErrorCode& status);
1261
1262 // Replace by using languageAlias.
1263 bool replaceLanguage(bool checkLanguage, bool checkRegion,
1264 bool checkVariants, UVector& toBeFreed,
1265 UErrorCode& status);
1266
1267 // Replace by using territoryAlias.
1268 bool replaceTerritory(UVector& toBeFreed, UErrorCode& status);
1269
1270 // Replace by using scriptAlias.
1271 bool replaceScript(UErrorCode& status);
1272
1273 // Replace by using variantAlias.
1274 bool replaceVariant(UErrorCode& status);
1275
1276 // Replace by using subdivisionAlias.
1277 bool replaceSubdivision(StringPiece subdivision,
1278 CharString& output, UErrorCode& status);
1279
1280 // Replace transformed extensions.
1281 bool replaceTransformedExtensions(
1282 CharString& transformedExtensions, CharString& output, UErrorCode& status);
1283};
1284
1285CharString&
1286AliasReplacer::generateKey(
1287 const char* language, const char* region, const char* variant,
1288 CharString& out, UErrorCode& status)
1289{
1290 if (U_FAILURE(status)) { return out; }
1291 out.append(language, status);
1292 if (notEmpty(region)) {
1293 out.append(SEP_CHAR'_', status)
1294 .append(region, status);
1295 }
1296 if (notEmpty(variant)) {
1297 out.append(SEP_CHAR'_', status)
1298 .append(variant, status);
1299 }
1300 return out;
1301}
1302
1303void
1304AliasReplacer::parseLanguageReplacement(
1305 const char* replacement,
1306 const char*& replacedLanguage,
1307 const char*& replacedScript,
1308 const char*& replacedRegion,
1309 const char*& replacedVariant,
1310 const char*& replacedExtensions,
1311 UVector& toBeFreed,
1312 UErrorCode& status)
1313{
1314 if (U_FAILURE(status)) {
1315 return;
1316 }
1317 replacedScript = replacedRegion = replacedVariant
1318 = replacedExtensions = nullptr;
1319 if (uprv_strchr(replacement, '_'):: strchr(replacement, '_') == nullptr) {
1320 replacedLanguage = replacement;
1321 // reach the end, just return it.
1322 return;
1323 }
1324 // We have multiple field so we have to allocate and parse
1325 CharString* str =
1326 new CharString(replacement, static_cast<int32_t>(uprv_strlen(replacement):: strlen(replacement)), status);
1327 LocalPointer<CharString> lpStr(str, status);
1328 toBeFreed.adoptElement(lpStr.orphan(), status);
1329 if (U_FAILURE(status)) {
1330 return;
1331 }
1332 char* data = str->data();
1333 replacedLanguage = (const char*) data;
1334 char* endOfField = uprv_strchr(data, '_'):: strchr(data, '_');
1335 *endOfField = '\0'; // null terminiate it.
1336 endOfField++;
1337 const char* start = endOfField;
1338 endOfField = const_cast<char*>(uprv_strchr(start, '_'):: strchr(start, '_'));
1339 size_t len = 0;
1340 if (endOfField == nullptr) {
1341 len = uprv_strlen(start):: strlen(start);
1342 } else {
1343 len = endOfField - start;
1344 *endOfField = '\0'; // null terminiate it.
1345 }
1346 if (len == 4 && uprv_isASCIILetter(*start)) {
1347 // Got a script
1348 replacedScript = start;
1349 if (endOfField == nullptr) {
1350 return;
1351 }
1352 start = endOfField++;
1353 endOfField = const_cast<char*>(uprv_strchr(start, '_'):: strchr(start, '_'));
1354 if (endOfField == nullptr) {
1355 len = uprv_strlen(start):: strlen(start);
1356 } else {
1357 len = endOfField - start;
1358 *endOfField = '\0'; // null terminiate it.
1359 }
1360 }
1361 if (len >= 2 && len <= 3) {
1362 // Got a region
1363 replacedRegion = start;
1364 if (endOfField == nullptr) {
1365 return;
1366 }
1367 start = endOfField++;
1368 endOfField = const_cast<char*>(uprv_strchr(start, '_'):: strchr(start, '_'));
1369 if (endOfField == nullptr) {
1370 len = uprv_strlen(start):: strlen(start);
1371 } else {
1372 len = endOfField - start;
1373 *endOfField = '\0'; // null terminiate it.
1374 }
1375 }
1376 if (len >= 4) {
1377 // Got a variant
1378 replacedVariant = start;
1379 if (endOfField == nullptr) {
1380 return;
1381 }
1382 start = endOfField++;
1383 }
1384 replacedExtensions = start;
1385}
1386
1387bool
1388AliasReplacer::replaceLanguage(
1389 bool checkLanguage, bool checkRegion,
1390 bool checkVariants, UVector& toBeFreed, UErrorCode& status)
1391{
1392 if (U_FAILURE(status)) {
1393 return false;
1394 }
1395 if ( (checkRegion && region == nullptr) ||
1396 (checkVariants && variants.size() == 0)) {
1397 // Nothing to search.
1398 return false;
1399 }
1400 int32_t variant_size = checkVariants ? variants.size() : 1;
1401 // Since we may have more than one variant, we need to loop through them.
1402 const char* searchLanguage = checkLanguage ? language : "und";
1403 const char* searchRegion = checkRegion ? region : nullptr;
1404 const char* searchVariant = nullptr;
1405 for (int32_t variant_index = 0;
1406 variant_index < variant_size;
1407 variant_index++) {
1408 if (checkVariants) {
1409 U_ASSERT(variant_index < variant_size)(static_cast <bool> (variant_index < variant_size) ?
void (0) : __assert_fail ("variant_index < variant_size",
__builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
1410 searchVariant = static_cast<const char*>(variants.elementAt(variant_index));
1411 }
1412
1413 if (searchVariant != nullptr && uprv_strlen(searchVariant):: strlen(searchVariant) < 4) {
1414 // Do not consider ill-formed variant subtag.
1415 searchVariant = nullptr;
1416 }
1417 CharString typeKey;
1418 generateKey(searchLanguage, searchRegion, searchVariant, typeKey,
1419 status);
1420 if (U_FAILURE(status)) {
1421 return false;
1422 }
1423 const char *replacement = data->languageMap().get(typeKey.data());
1424 if (replacement == nullptr) {
1425 // Found no replacement data.
1426 continue;
1427 }
1428
1429 const char* replacedLanguage = nullptr;
1430 const char* replacedScript = nullptr;
1431 const char* replacedRegion = nullptr;
1432 const char* replacedVariant = nullptr;
1433 const char* replacedExtensions = nullptr;
1434 parseLanguageReplacement(replacement,
1435 replacedLanguage,
1436 replacedScript,
1437 replacedRegion,
1438 replacedVariant,
1439 replacedExtensions,
1440 toBeFreed,
1441 status);
1442 replacedLanguage =
1443 (replacedLanguage != nullptr && uprv_strcmp(replacedLanguage, "und"):: strcmp(replacedLanguage, "und") == 0) ?
1444 language : replacedLanguage;
1445 replacedScript = deleteOrReplace(script, nullptr, replacedScript);
1446 replacedRegion = deleteOrReplace(region, searchRegion, replacedRegion);
1447 replacedVariant = deleteOrReplace(
1448 searchVariant, searchVariant, replacedVariant);
1449
1450 if ( same(language, replacedLanguage) &&
1451 same(script, replacedScript) &&
1452 same(region, replacedRegion) &&
1453 same(searchVariant, replacedVariant) &&
1454 replacedExtensions == nullptr) {
1455 // Replacement produce no changes.
1456 continue;
1457 }
1458
1459 language = replacedLanguage;
1460 region = replacedRegion;
1461 script = replacedScript;
1462 if (searchVariant != nullptr) {
1463 if (notEmpty(replacedVariant)) {
1464 variants.setElementAt((void*)replacedVariant, variant_index);
1465 } else {
1466 variants.removeElementAt(variant_index);
1467 }
1468 }
1469 if (replacedExtensions != nullptr) {
1470 // DO NOTHING
1471 // UTS35 does not specify what should we do if we have extensions in the
1472 // replacement. Currently we know only the following 4 "BCP47 LegacyRules" have
1473 // extensions in them languageAlias:
1474 // i_default => en_x_i_default
1475 // i_enochian => und_x_i_enochian
1476 // i_mingo => see_x_i_mingo
1477 // zh_min => nan_x_zh_min
1478 // But all of them are already changed by code inside ultag_parse() before
1479 // hitting this code.
1480 }
1481
1482 // Something changed by language alias data.
1483 return true;
1484 }
1485 // Nothing changed by language alias data.
1486 return false;
1487}
1488
1489bool
1490AliasReplacer::replaceTerritory(UVector& toBeFreed, UErrorCode& status)
1491{
1492 if (U_FAILURE(status)) {
1493 return false;
1494 }
1495 if (region == nullptr) {
1496 // No region to search.
1497 return false;
1498 }
1499 const char *replacement = data->territoryMap().get(region);
1500 if (replacement == nullptr) {
1501 // Found no replacement data for this region.
1502 return false;
1503 }
1504 const char* replacedRegion = replacement;
1505 const char* firstSpace = uprv_strchr(replacement, ' '):: strchr(replacement, ' ');
1506 if (firstSpace != nullptr) {
1507 // If there are are more than one region in the replacement.
1508 // We need to check which one match based on the language.
1509 // Cannot use nullptr for language because that will construct
1510 // the default locale, in that case, use "und" to get the correct
1511 // locale.
1512 Locale l = LocaleBuilder()
1513 .setLanguage(language == nullptr ? "und" : language)
1514 .setScript(script)
1515 .build(status);
1516 l.addLikelySubtags(status);
1517 const char* likelyRegion = l.getCountry();
1518 LocalPointer<CharString> item;
1519 if (likelyRegion != nullptr && uprv_strlen(likelyRegion):: strlen(likelyRegion) > 0) {
1520 size_t len = uprv_strlen(likelyRegion):: strlen(likelyRegion);
1521 const char* foundInReplacement = uprv_strstr(replacement,:: strstr(replacement, likelyRegion)
1522 likelyRegion):: strstr(replacement, likelyRegion);
1523 if (foundInReplacement != nullptr) {
1524 // Assuming the case there are no three letter region code in
1525 // the replacement of territoryAlias
1526 U_ASSERT(foundInReplacement == replacement ||(static_cast <bool> (foundInReplacement == replacement ||
*(foundInReplacement-1) == ' ') ? void (0) : __assert_fail (
"foundInReplacement == replacement || *(foundInReplacement-1) == ' '"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
1527 *(foundInReplacement-1) == ' ')(static_cast <bool> (foundInReplacement == replacement ||
*(foundInReplacement-1) == ' ') ? void (0) : __assert_fail (
"foundInReplacement == replacement || *(foundInReplacement-1) == ' '"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
1528 U_ASSERT(foundInReplacement[len] == ' ' ||(static_cast <bool> (foundInReplacement[len] == ' ' || foundInReplacement
[len] == '\0') ? void (0) : __assert_fail ("foundInReplacement[len] == ' ' || foundInReplacement[len] == '\\0'"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
1529 foundInReplacement[len] == '\0')(static_cast <bool> (foundInReplacement[len] == ' ' || foundInReplacement
[len] == '\0') ? void (0) : __assert_fail ("foundInReplacement[len] == ' ' || foundInReplacement[len] == '\\0'"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
1530 item.adoptInsteadAndCheckErrorCode(
1531 new CharString(foundInReplacement, static_cast<int32_t>(len), status), status);
1532 }
1533 }
1534 if (item.isNull() && U_SUCCESS(status)) {
1535 item.adoptInsteadAndCheckErrorCode(
1536 new CharString(replacement,
1537 static_cast<int32_t>(firstSpace - replacement), status), status);
1538 }
1539 if (U_FAILURE(status)) { return false; }
1540 replacedRegion = item->data();
1541 toBeFreed.adoptElement(item.orphan(), status);
1542 if (U_FAILURE(status)) { return false; }
1543 }
1544 U_ASSERT(!same(region, replacedRegion))(static_cast <bool> (!same(region, replacedRegion)) ? void
(0) : __assert_fail ("!same(region, replacedRegion)", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
1545 region = replacedRegion;
1546 // The region is changed by data in territory alias.
1547 return true;
1548}
1549
1550bool
1551AliasReplacer::replaceScript(UErrorCode& status)
1552{
1553 if (U_FAILURE(status)) {
1554 return false;
1555 }
1556 if (script == nullptr) {
1557 // No script to search.
1558 return false;
1559 }
1560 const char *replacement = data->scriptMap().get(script);
1561 if (replacement == nullptr) {
1562 // Found no replacement data for this script.
1563 return false;
1564 }
1565 U_ASSERT(!same(script, replacement))(static_cast <bool> (!same(script, replacement)) ? void
(0) : __assert_fail ("!same(script, replacement)", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
1566 script = replacement;
1567 // The script is changed by data in script alias.
1568 return true;
1569}
1570
1571bool
1572AliasReplacer::replaceVariant(UErrorCode& status)
1573{
1574 if (U_FAILURE(status)) {
1575 return false;
1576 }
1577 // Since we may have more than one variant, we need to loop through them.
1578 for (int32_t i = 0; i < variants.size(); i++) {
1579 const char* variant = static_cast<const char*>(variants.elementAt(i));
1580 const char *replacement = data->variantMap().get(variant);
1581 if (replacement == nullptr) {
1582 // Found no replacement data for this variant.
1583 continue;
1584 }
1585 U_ASSERT((uprv_strlen(replacement) >= 5 &&(static_cast <bool> ((:: strlen(replacement) >= 5 &&
:: strlen(replacement) <= 8) || (:: strlen(replacement) ==
4 && replacement[0] >= '0' && replacement
[0] <= '9')) ? void (0) : __assert_fail ("(:: strlen(replacement) >= 5 && :: strlen(replacement) <= 8) || (:: strlen(replacement) == 4 && replacement[0] >= '0' && replacement[0] <= '9')"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
1586 uprv_strlen(replacement) <= 8) ||(static_cast <bool> ((:: strlen(replacement) >= 5 &&
:: strlen(replacement) <= 8) || (:: strlen(replacement) ==
4 && replacement[0] >= '0' && replacement
[0] <= '9')) ? void (0) : __assert_fail ("(:: strlen(replacement) >= 5 && :: strlen(replacement) <= 8) || (:: strlen(replacement) == 4 && replacement[0] >= '0' && replacement[0] <= '9')"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
1587 (uprv_strlen(replacement) == 4 &&(static_cast <bool> ((:: strlen(replacement) >= 5 &&
:: strlen(replacement) <= 8) || (:: strlen(replacement) ==
4 && replacement[0] >= '0' && replacement
[0] <= '9')) ? void (0) : __assert_fail ("(:: strlen(replacement) >= 5 && :: strlen(replacement) <= 8) || (:: strlen(replacement) == 4 && replacement[0] >= '0' && replacement[0] <= '9')"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
1588 replacement[0] >= '0' &&(static_cast <bool> ((:: strlen(replacement) >= 5 &&
:: strlen(replacement) <= 8) || (:: strlen(replacement) ==
4 && replacement[0] >= '0' && replacement
[0] <= '9')) ? void (0) : __assert_fail ("(:: strlen(replacement) >= 5 && :: strlen(replacement) <= 8) || (:: strlen(replacement) == 4 && replacement[0] >= '0' && replacement[0] <= '9')"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
1589 replacement[0] <= '9'))(static_cast <bool> ((:: strlen(replacement) >= 5 &&
:: strlen(replacement) <= 8) || (:: strlen(replacement) ==
4 && replacement[0] >= '0' && replacement
[0] <= '9')) ? void (0) : __assert_fail ("(:: strlen(replacement) >= 5 && :: strlen(replacement) <= 8) || (:: strlen(replacement) == 4 && replacement[0] >= '0' && replacement[0] <= '9')"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
1590 if (!same(variant, replacement)) {
1591 variants.setElementAt((void*)replacement, i);
1592 // Special hack to handle hepburn-heploc => alalc97
1593 if (uprv_strcmp(variant, "heploc"):: strcmp(variant, "heploc") == 0) {
1594 for (int32_t j = 0; j < variants.size(); j++) {
1595 if (uprv_strcmp((const char*)(variants.elementAt(j)),:: strcmp((const char*)(variants.elementAt(j)), "hepburn")
1596 "hepburn"):: strcmp((const char*)(variants.elementAt(j)), "hepburn") == 0) {
1597 variants.removeElementAt(j);
1598 }
1599 }
1600 }
1601 return true;
1602 }
1603 }
1604 return false;
1605}
1606
1607bool
1608AliasReplacer::replaceSubdivision(
1609 StringPiece subdivision, CharString& output, UErrorCode& status)
1610{
1611 if (U_FAILURE(status)) {
1612 return false;
1613 }
1614 const char *replacement = data->subdivisionMap().get(subdivision.data());
1615 if (replacement != nullptr) {
1616 const char* firstSpace = uprv_strchr(replacement, ' '):: strchr(replacement, ' ');
1617 // Found replacement data for this subdivision.
1618 size_t len = (firstSpace != nullptr) ?
1619 (firstSpace - replacement) : uprv_strlen(replacement):: strlen(replacement);
1620 if (2 <= len && len <= 8) {
1621 output.append(replacement, static_cast<int32_t>(len), status);
1622 if (2 == len) {
1623 // Add 'zzzz' based on changes to UTS #35 for CLDR-14312.
1624 output.append("zzzz", 4, status);
1625 }
1626 }
1627 return true;
1628 }
1629 return false;
1630}
1631
1632bool
1633AliasReplacer::replaceTransformedExtensions(
1634 CharString& transformedExtensions, CharString& output, UErrorCode& status)
1635{
1636 // The content of the transformedExtensions will be modified in this
1637 // function to NUL-terminating (tkey-tvalue) pairs.
1638 if (U_FAILURE(status)) {
1639 return false;
1640 }
1641 int32_t len = transformedExtensions.length();
1642 const char* str = transformedExtensions.data();
1643 const char* tkey = ultag_getTKeyStart(str);
1644 int32_t tlangLen = (tkey == str) ? 0 :
1645 ((tkey == nullptr) ? len : static_cast<int32_t>((tkey - str - 1)));
1646 if (tlangLen > 0) {
1647 Locale tlang = LocaleBuilder()
1648 .setLanguageTag(StringPiece(str, tlangLen))
1649 .build(status);
1650 tlang.canonicalize(status);
1651 output = tlang.toLanguageTag<CharString>(status);
1652 if (U_FAILURE(status)) {
1653 return false;
1654 }
1655 T_CString_toLowerCase(output.data());
1656 }
1657 if (tkey != nullptr) {
1658 // We need to sort the tfields by tkey
1659 UVector tfields(status);
1660 if (U_FAILURE(status)) {
1661 return false;
1662 }
1663 do {
1664 const char* tvalue = uprv_strchr(tkey, '-'):: strchr(tkey, '-');
1665 if (tvalue == nullptr) {
1666 status = U_ILLEGAL_ARGUMENT_ERROR;
1667 return false;
1668 }
1669 const char* nextTKey = ultag_getTKeyStart(tvalue);
1670 if (nextTKey != nullptr) {
1671 *const_cast<char*>(nextTKey - 1) = '\0'; // NUL terminate tvalue
1672 }
1673 tfields.insertElementAt((void*)tkey, tfields.size(), status);
1674 if (U_FAILURE(status)) {
1675 return false;
1676 }
1677 tkey = nextTKey;
1678 } while (tkey != nullptr);
1679 tfields.sort([](UElement e1, UElement e2) -> int32_t {
1680 return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer):: strcmp((const char*)e1.pointer, (const char*)e2.pointer);
1681 }, status);
1682 for (int32_t i = 0; i < tfields.size(); i++) {
1683 if (output.length() > 0) {
1684 output.append('-', status);
1685 }
1686 const char* tfield = static_cast<const char*>(tfields.elementAt(i));
1687 const char* tvalue = uprv_strchr(tfield, '-'):: strchr(tfield, '-');
1688 if (tvalue == nullptr) {
1689 status = U_ILLEGAL_ARGUMENT_ERROR;
1690 return false;
1691 }
1692 // Split the "tkey-tvalue" pair string so that we can canonicalize the tvalue.
1693 *const_cast<char*>(tvalue++) = '\0'; // NUL terminate tkey
1694 output.append(tfield, status).append('-', status);
1695 std::optional<std::string_view> bcpTValue = ulocimp_toBcpType(tfield, tvalue);
1696 output.append(bcpTValue.has_value() ? *bcpTValue : tvalue, status);
1697 }
1698 }
1699 if (U_FAILURE(status)) {
1700 return false;
1701 }
1702 return true;
1703}
1704
1705CharString&
1706AliasReplacer::outputToString(
1707 CharString& out, UErrorCode& status)
1708{
1709 if (U_FAILURE(status)) { return out; }
1710 out.append(language, status);
1711 if (notEmpty(script)) {
1712 out.append(SEP_CHAR'_', status)
1713 .append(script, status);
1714 }
1715 if (notEmpty(region)) {
1716 out.append(SEP_CHAR'_', status)
1717 .append(region, status);
1718 }
1719 if (variants.size() > 0) {
1720 if (!notEmpty(script) && !notEmpty(region)) {
1721 out.append(SEP_CHAR'_', status);
1722 }
1723 variants.sort([](UElement e1, UElement e2) -> int32_t {
1724 return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer):: strcmp((const char*)e1.pointer, (const char*)e2.pointer);
1725 }, status);
1726 int32_t variantsStart = out.length();
1727 for (int32_t i = 0; i < variants.size(); i++) {
1728 out.append(SEP_CHAR'_', status)
1729 .append(static_cast<const char*>(variants.elementAt(i)),
1730 status);
1731 }
1732 T_CString_toUpperCase(out.data() + variantsStart);
1733 }
1734 if (notEmpty(extensions)) {
1735 CharString tmp("und_", status);
1736 tmp.append(extensions, status);
1737 Locale tmpLocale(tmp.data());
1738 // only support x extension inside CLDR for now.
1739 U_ASSERT(extensions[0] == 'x')(static_cast <bool> (extensions[0] == 'x') ? void (0) :
__assert_fail ("extensions[0] == 'x'", __builtin_FILE (), __builtin_LINE
(), __extension__ __PRETTY_FUNCTION__))
;
1740 out.append(tmpLocale.getName() + 1, status);
1741 }
1742 return out;
1743}
1744
1745bool
1746AliasReplacer::replace(const Locale& locale, CharString& out, UErrorCode& status)
1747{
1748 data = AliasData::singleton(status);
1749 if (U_FAILURE(status)) {
1750 return false;
1751 }
1752 U_ASSERT(data != nullptr)(static_cast <bool> (data != nullptr) ? void (0) : __assert_fail
("data != nullptr", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
;
1753 out.clear();
1754 language = locale.getLanguage();
1755 if (!notEmpty(language)) {
1756 language = nullptr;
1757 }
1758 script = locale.getScript();
1759 if (!notEmpty(script)) {
1760 script = nullptr;
1761 }
1762 region = locale.getCountry();
1763 if (!notEmpty(region)) {
1764 region = nullptr;
1765 }
1766 const char* variantsStr = locale.getVariant();
1767 CharString variantsBuff(variantsStr, -1, status);
1768 if (!variantsBuff.isEmpty()) {
1769 if (U_FAILURE(status)) { return false; }
1770 char* start = variantsBuff.data();
1771 T_CString_toLowerCase(start);
1772 char* end;
1773 while ((end = uprv_strchr(start, SEP_CHAR):: strchr(start, '_')) != nullptr &&
1774 U_SUCCESS(status)) {
1775 *end = NULL_CHAR'\0'; // null terminate inside variantsBuff
1776 // do not add "" or duplicate data to variants
1777 if (*start && !variants.contains(start)) {
1778 variants.addElement(start, status);
1779 }
1780 start = end + 1;
1781 }
1782 // do not add "" or duplicate data to variants
1783 if (*start && !variants.contains(start)) {
1784 variants.addElement(start, status);
1785 }
1786 }
1787 if (U_FAILURE(status)) { return false; }
1788
1789 // Sort the variants
1790 variants.sort([](UElement e1, UElement e2) -> int32_t {
1791 return uprv_strcmp((const char*)e1.pointer, (const char*)e2.pointer):: strcmp((const char*)e1.pointer, (const char*)e2.pointer);
1792 }, status);
1793
1794 // A changed count to assert when loop too many times.
1795 int changed = 0;
1796 // A UVector to to hold CharString allocated by the replace* method
1797 // and freed when out of scope from his function.
1798 UVector stringsToBeFreed([](void *obj) { delete static_cast<CharString*>(obj); },
1799 nullptr, 10, status);
1800 while (U_SUCCESS(status)) {
1801 // Something wrong with the data cause looping here more than 10 times
1802 // already.
1803 U_ASSERT(changed < 5)(static_cast <bool> (changed < 5) ? void (0) : __assert_fail
("changed < 5", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
;
1804 // From observation of key in data/misc/metadata.txt
1805 // we know currently we only need to search in the following combination
1806 // of fields for type in languageAlias:
1807 // * lang_region_variant
1808 // * lang_region
1809 // * lang_variant
1810 // * lang
1811 // * und_variant
1812 // This assumption is ensured by the U_ASSERT in readLanguageAlias
1813 //
1814 // lang REGION variant
1815 if ( replaceLanguage(true, true, true, stringsToBeFreed, status) ||
1816 replaceLanguage(true, true, false, stringsToBeFreed, status) ||
1817 replaceLanguage(true, false, true, stringsToBeFreed, status) ||
1818 replaceLanguage(true, false, false, stringsToBeFreed, status) ||
1819 replaceLanguage(false,false, true, stringsToBeFreed, status) ||
1820 replaceTerritory(stringsToBeFreed, status) ||
1821 replaceScript(status) ||
1822 replaceVariant(status)) {
1823 // Some values in data is changed, try to match from the beginning
1824 // again.
1825 changed++;
1826 continue;
1827 }
1828 // Nothing changed. Break out.
1829 break;
1830 } // while(1)
1831
1832 if (U_FAILURE(status)) { return false; }
1833 // Nothing changed and we know the order of the variants are not change
1834 // because we have no variant or only one.
1835 const char* extensionsStr = locale_getKeywordsStart(locale.getName());
1836 if (changed == 0 && variants.size() <= 1 && extensionsStr == nullptr) {
1837 return false;
1838 }
1839 outputToString(out, status);
1840 if (U_FAILURE(status)) {
1841 return false;
1842 }
1843 if (extensionsStr != nullptr) {
1844 changed = 0;
1845 Locale temp(locale);
1846 LocalPointer<icu::StringEnumeration> iter(locale.createKeywords(status));
1847 if (U_SUCCESS(status) && !iter.isNull()) {
1848 const char* key;
1849 while ((key = iter->next(nullptr, status)) != nullptr) {
1850 if (uprv_strcmp("sd", key):: strcmp("sd", key) == 0 || uprv_strcmp("rg", key):: strcmp("rg", key) == 0 ||
1851 uprv_strcmp("t", key):: strcmp("t", key) == 0) {
1852 auto value = locale.getKeywordValue<CharString>(key, status);
1853 if (U_FAILURE(status)) {
1854 status = U_ZERO_ERROR;
1855 continue;
1856 }
1857 CharString replacement;
1858 if (uprv_strlen(key):: strlen(key) == 2) {
1859 if (replaceSubdivision(value.toStringPiece(), replacement, status)) {
1860 changed++;
1861 temp.setKeywordValue(key, replacement.data(), status);
1862 }
1863 } else {
1864 U_ASSERT(uprv_strcmp(key, "t") == 0)(static_cast <bool> (:: strcmp(key, "t") == 0) ? void (
0) : __assert_fail (":: strcmp(key, \"t\") == 0", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
1865 if (replaceTransformedExtensions(value, replacement, status)) {
1866 changed++;
1867 temp.setKeywordValue(key, replacement.data(), status);
1868 }
1869 }
1870 if (U_FAILURE(status)) {
1871 return false;
1872 }
1873 }
1874 }
1875 }
1876 if (changed != 0) {
1877 extensionsStr = locale_getKeywordsStart(temp.getName());
1878 }
1879 out.append(extensionsStr, status);
1880 }
1881 if (U_FAILURE(status)) {
1882 return false;
1883 }
1884 // If the tag is not changed, return.
1885 if (uprv_strcmp(out.data(), locale.getName()):: strcmp(out.data(), locale.getName()) == 0) {
1886 out.clear();
1887 return false;
1888 }
1889 return true;
1890}
1891
1892// Return true if the locale is changed during canonicalization.
1893// The replaced value then will be put into out.
1894bool
1895canonicalizeLocale(const Locale& locale, CharString& out, UErrorCode& status)
1896{
1897 if (U_FAILURE(status)) { return false; }
1898 AliasReplacer replacer(status);
1899 return replacer.replace(locale, out, status);
1900}
1901
1902// Function to optimize for known cases without so we can skip the loading
1903// of resources in the startup time until we really need it.
1904bool
1905isKnownCanonicalizedLocale(const char* locale, UErrorCode& status)
1906{
1907 if (U_FAILURE(status)) { return false; }
1908
1909 if ( uprv_strcmp(locale, "c"):: strcmp(locale, "c") == 0 ||
1910 uprv_strcmp(locale, "en"):: strcmp(locale, "en") == 0 ||
1911 uprv_strcmp(locale, "en_US"):: strcmp(locale, "en_US") == 0) {
1912 return true;
1913 }
1914
1915 // common well-known Canonicalized.
1916 umtx_initOnce(gKnownCanonicalizedInitOnce,
1917 &loadKnownCanonicalized, status);
1918 if (U_FAILURE(status)) {
1919 return false;
1920 }
1921 U_ASSERT(gKnownCanonicalized != nullptr)(static_cast <bool> (gKnownCanonicalized != nullptr) ? void
(0) : __assert_fail ("gKnownCanonicalized != nullptr", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
1922 return uhash_geti(gKnownCanonicalized, locale) != 0;
1923}
1924
1925} // namespace
1926
1927U_NAMESPACE_END}
1928
1929// Function for testing.
1930U_EXPORT const char* const*
1931ulocimp_getKnownCanonicalizedLocaleForTest(int32_t& length)
1932{
1933 U_NAMESPACE_USEusing namespace icu_78;
1934 length = UPRV_LENGTHOF(KNOWN_CANONICALIZED)(int32_t)(sizeof(KNOWN_CANONICALIZED)/sizeof((KNOWN_CANONICALIZED
)[0]))
;
1935 return KNOWN_CANONICALIZED;
1936}
1937
1938// Function for testing.
1939U_EXPORT bool
1940ulocimp_isCanonicalizedLocaleForTest(const char* localeName)
1941{
1942 U_NAMESPACE_USEusing namespace icu_78;
1943 Locale l(localeName);
1944 UErrorCode status = U_ZERO_ERROR;
1945 CharString temp;
1946 return !canonicalizeLocale(l, temp, status) && U_SUCCESS(status);
1947}
1948
1949U_NAMESPACE_BEGINnamespace icu_78 {
1950
1951Locale& Locale::init(const char* localeID, UBool canonicalize)
1952{
1953 return localeID == nullptr ? *this = getDefault() : init(StringPiece{localeID}, canonicalize);
1954}
1955
1956/*This function initializes a Locale from a C locale ID*/
1957Locale& Locale::init(StringPiece localeID, UBool canonicalize)
1958{
1959 /* Free our current storage */
1960 Nest& nest = payload.emplace<Nest>();
1961
1962 // not a loop:
1963 // just an easy way to have a common error-exit
1964 // without goto and without another function
1965 do {
1966 char *separator;
1967 char *field[5] = {nullptr};
1968 int32_t fieldLen[5] = {0};
1969 int32_t fieldIdx;
1970 int32_t variantField;
1971 int32_t length;
1972 UErrorCode err;
1973
1974 const auto parse = [canonicalize](std::string_view localeID,
1975 char* name,
1976 int32_t nameCapacity,
1977 UErrorCode& status) {
1978 return ByteSinkUtil::viaByteSinkToTerminatedChars(
1979 name, nameCapacity,
1980 [&](ByteSink& sink, UErrorCode& status) {
1981 if (canonicalize) {
1982 ulocimp_canonicalize(localeID, sink, status);
1983 } else {
1984 ulocimp_getName(localeID, sink, status);
1985 }
1986 },
1987 status);
1988 };
1989
1990 // "canonicalize" the locale ID to ICU/Java format
1991 char* fullName = nest.baseName;
1992 err = U_ZERO_ERROR;
1993 length = parse(localeID, fullName, sizeof Nest::baseName, err);
1994
1995 FixedString fullNameBuffer;
1996 if (err == U_BUFFER_OVERFLOW_ERROR || length >= static_cast<int32_t>(sizeof Nest::baseName)) {
1997 /*Go to heap for the fullName if necessary*/
1998 if (!fullNameBuffer.reserve(length + 1)) {
1999 break; // error: out of memory
2000 }
2001 fullName = fullNameBuffer.getAlias();
2002 err = U_ZERO_ERROR;
2003 length = parse(localeID, fullName, length + 1, err);
2004 }
2005 if(U_FAILURE(err) || err == U_STRING_NOT_TERMINATED_WARNING) {
2006 /* should never occur */
2007 break;
2008 }
2009
2010 std::string_view language;
2011 std::string_view script;
2012 std::string_view region;
2013 int32_t variantBegin = length;
2014
2015 /* after uloc_getName/canonicalize() we know that only '_' are separators */
2016 /* But _ could also appeared in timezone such as "en@timezone=America/Los_Angeles" */
2017 separator = field[0] = fullName;
2018 fieldIdx = 1;
2019 char* at = uprv_strchr(fullName, '@'):: strchr(fullName, '@');
2020 while ((separator = uprv_strchr(field[fieldIdx-1], SEP_CHAR):: strchr(field[fieldIdx-1], '_')) != nullptr &&
2021 fieldIdx < UPRV_LENGTHOF(field)(int32_t)(sizeof(field)/sizeof((field)[0]))-1 &&
2022 (at == nullptr || separator < at)) {
2023 field[fieldIdx] = separator + 1;
2024 fieldLen[fieldIdx - 1] = static_cast<int32_t>(separator - field[fieldIdx - 1]);
2025 fieldIdx++;
2026 }
2027 // variant may contain @foo or .foo POSIX cruft; remove it
2028 separator = uprv_strchr(field[fieldIdx-1], '@'):: strchr(field[fieldIdx-1], '@');
2029 char* sep2 = uprv_strchr(field[fieldIdx-1], '.'):: strchr(field[fieldIdx-1], '.');
2030 if (separator!=nullptr || sep2!=nullptr) {
2031 if (separator==nullptr || (sep2!=nullptr && separator > sep2)) {
2032 separator = sep2;
2033 }
2034 fieldLen[fieldIdx - 1] = static_cast<int32_t>(separator - field[fieldIdx - 1]);
2035 } else {
2036 fieldLen[fieldIdx - 1] = length - static_cast<int32_t>(field[fieldIdx - 1] - fullName);
2037 }
2038 bool hasKeywords = at != nullptr && uprv_strchr(at + 1, '='):: strchr(at + 1, '=') != nullptr;
2039
2040 if (fieldLen[0] >= ULOC_LANG_CAPACITY12)
2041 {
2042 break; // error: the language field is too long
2043 }
2044
2045 variantField = 1; /* Usually the 2nd one, except when a script or country is also used. */
2046 if (fieldLen[0] > 0) {
2047 /* We have a language */
2048 language = {fullName, static_cast<std::string_view::size_type>(fieldLen[0])};
2049 }
2050 if (fieldLen[1] == 4 && uprv_isASCIILetter(field[1][0]) &&
2051 uprv_isASCIILetter(field[1][1]) && uprv_isASCIILetter(field[1][2]) &&
2052 uprv_isASCIILetter(field[1][3])) {
2053 /* We have at least a script */
2054 script = {field[1], static_cast<std::string_view::size_type>(fieldLen[1])};
2055 variantField++;
2056 }
2057
2058 if (fieldLen[variantField] == 2 || fieldLen[variantField] == 3) {
2059 /* We have a country */
2060 region = {field[variantField], static_cast<std::string_view::size_type>(fieldLen[variantField])};
2061 variantField++;
2062 } else if (fieldLen[variantField] == 0) {
2063 variantField++; /* script or country empty but variant in next field (i.e. en__POSIX) */
2064 }
2065
2066 if (fieldLen[variantField] > 0) {
2067 /* We have a variant */
2068 variantBegin = static_cast<int32_t>(field[variantField] - fullName);
2069 } else if (hasKeywords) {
2070 // The original computation of variantBegin leaves it equal to the length
2071 // of fullName if there is no variant. It should instead be
2072 // the length of the baseName.
2073 variantBegin = static_cast<int32_t>(at - fullName);
2074 }
2075
2076 if (!hasKeywords && Nest::fits(length, language, script, region)) {
2077 U_ASSERT(fullName == nest.baseName)(static_cast <bool> (fullName == nest.baseName) ? void (
0) : __assert_fail ("fullName == nest.baseName", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
2078 U_ASSERT(fullNameBuffer.isEmpty())(static_cast <bool> (fullNameBuffer.isEmpty()) ? void (
0) : __assert_fail ("fullNameBuffer.isEmpty()", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
2079 nest.init(language, script, region, variantBegin);
2080 } else {
2081 if (fullName == nest.baseName) {
2082 U_ASSERT(fullNameBuffer.isEmpty())(static_cast <bool> (fullNameBuffer.isEmpty()) ? void (
0) : __assert_fail ("fullNameBuffer.isEmpty()", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
2083 fullNameBuffer = {fullName, static_cast<std::string_view::size_type>(length)};
2084 if (fullNameBuffer.isEmpty()) {
2085 break; // error: out of memory
2086 }
2087 if (!language.empty()) {
2088 language = {fullNameBuffer.data(), language.size()};
2089 }
2090 if (!script.empty()) {
2091 script = {fullNameBuffer.data() + (script.data() - fullName), script.size()};
2092 }
2093 if (!region.empty()) {
2094 region = {fullNameBuffer.data() + (region.data() - fullName), region.size()};
2095 }
2096 }
2097 Heap& heap = payload.emplace<Heap>(language, script, region, variantBegin);
2098 if (isBogus()) {
2099 break; // error: out of memory
2100 }
2101 U_ASSERT(!fullNameBuffer.isEmpty())(static_cast <bool> (!fullNameBuffer.isEmpty()) ? void (
0) : __assert_fail ("!fullNameBuffer.isEmpty()", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__))
;
2102 heap.ptr->fullName = std::move(fullNameBuffer);
2103 if (hasKeywords) {
2104 if (std::string_view::size_type baseNameLength = at - fullName; baseNameLength > 0) {
2105 heap.ptr->baseName = {heap.ptr->fullName.data(), baseNameLength};
2106 if (heap.ptr->baseName.isEmpty()) {
2107 break; // error: out of memory
2108 }
2109 }
2110 }
2111 }
2112
2113 if (canonicalize) {
2114 if (!isKnownCanonicalizedLocale(getName(), err)) {
2115 CharString replaced;
2116 // Not sure it is already canonicalized
2117 if (canonicalizeLocale(*this, replaced, err)) {
2118 U_ASSERT(U_SUCCESS(err))(static_cast <bool> (U_SUCCESS(err)) ? void (0) : __assert_fail
("U_SUCCESS(err)", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
;
2119 // If need replacement, call init again.
2120 init(replaced.data(), false);
2121 }
2122 if (U_FAILURE(err)) {
2123 break;
2124 }
2125 }
2126 } // if (canonicalize) {
2127
2128 // successful end of init()
2129 return *this;
2130 } while(0); /*loop doesn't iterate*/
2131
2132 // when an error occurs, then set this object to "bogus" (there is no UErrorCode here)
2133 setToBogus();
2134
2135 return *this;
2136}
2137
2138int32_t
2139Locale::hashCode() const
2140{
2141 return ustr_hashCharsN(getName(), static_cast<int32_t>(uprv_strlen(getName()):: strlen(getName())));
2142}
2143
2144void
2145Locale::setToBogus() {
2146 /* Free our current storage */
2147 payload.setToBogus();
2148}
2149
2150const Locale& U_EXPORT2
2151Locale::getDefault()
2152{
2153 {
2154 Mutex lock(&gDefaultLocaleMutex);
2155 if (gDefaultLocale != nullptr) {
2156 return *gDefaultLocale;
2157 }
2158 }
2159 UErrorCode status = U_ZERO_ERROR;
2160 return *locale_set_default_internal(nullptr, status);
2161}
2162
2163
2164
2165void U_EXPORT2
2166Locale::setDefault( const Locale& newLocale,
2167 UErrorCode& status)
2168{
2169 if (U_FAILURE(status)) {
2170 return;
2171 }
2172
2173 /* Set the default from the full name string of the supplied locale.
2174 * This is a convenient way to access the default locale caching mechanisms.
2175 */
2176 const char *localeID = newLocale.getName();
2177 locale_set_default_internal(localeID, status);
2178}
2179
2180void
2181Locale::addLikelySubtags(UErrorCode& status) {
2182 if (U_FAILURE(status)) {
2183 return;
2184 }
2185
2186 CharString maximizedLocaleID = ulocimp_addLikelySubtags(getName(), status);
2187
2188 if (U_FAILURE(status)) {
2189 if (status == U_MEMORY_ALLOCATION_ERROR) {
2190 setToBogus();
2191 }
2192 return;
2193 }
2194
2195 init(maximizedLocaleID.data(), /*canonicalize=*/false);
2196 if (isBogus()) {
2197 status = U_ILLEGAL_ARGUMENT_ERROR;
2198 }
2199}
2200
2201void
2202Locale::minimizeSubtags(UErrorCode& status) {
2203 Locale::minimizeSubtags(false, status);
2204}
2205void
2206Locale::minimizeSubtags(bool favorScript, UErrorCode& status) {
2207 if (U_FAILURE(status)) {
2208 return;
2209 }
2210
2211 CharString minimizedLocaleID = ulocimp_minimizeSubtags(getName(), favorScript, status);
2212
2213 if (U_FAILURE(status)) {
2214 if (status == U_MEMORY_ALLOCATION_ERROR) {
2215 setToBogus();
2216 }
2217 return;
2218 }
2219
2220 init(minimizedLocaleID.data(), /*canonicalize=*/false);
2221 if (isBogus()) {
2222 status = U_ILLEGAL_ARGUMENT_ERROR;
2223 }
2224}
2225
2226void
2227Locale::canonicalize(UErrorCode& status) {
2228 if (U_FAILURE(status)) {
2229 return;
2230 }
2231 if (isBogus()) {
2232 status = U_ILLEGAL_ARGUMENT_ERROR;
2233 return;
2234 }
2235 CharString uncanonicalized(getName(), status);
2236 if (U_FAILURE(status)) {
2237 if (status == U_MEMORY_ALLOCATION_ERROR) {
2238 setToBogus();
2239 }
2240 return;
2241 }
2242 init(uncanonicalized.data(), /*canonicalize=*/true);
2243 if (isBogus()) {
2244 status = U_ILLEGAL_ARGUMENT_ERROR;
2245 }
2246}
2247
2248Locale U_EXPORT2
2249Locale::forLanguageTag(StringPiece tag, UErrorCode& status)
2250{
2251 Locale result(Locale::eBOGUS);
2252
2253 if (U_FAILURE(status)) {
2254 return result;
2255 }
2256
2257 // If a BCP 47 language tag is passed as the language parameter to the
2258 // normal Locale constructor, it will actually fall back to invoking
2259 // uloc_forLanguageTag() to parse it if it somehow is able to detect that
2260 // the string actually is BCP 47. This works well for things like strings
2261 // using BCP 47 extensions, but it does not at all work for things like
2262 // legacy language tags (marked as “Type: grandfathered” in BCP 47,
2263 // e.g., "en-GB-oed") which are possible to also
2264 // interpret as ICU locale IDs and because of that won't trigger the BCP 47
2265 // parsing. Therefore the code here explicitly calls uloc_forLanguageTag()
2266 // and then Locale::init(), instead of just calling the normal constructor.
2267
2268 int32_t parsedLength;
2269 CharString localeID = ulocimp_forLanguageTag(
2270 tag.data(),
2271 tag.length(),
2272 &parsedLength,
2273 status);
2274
2275 if (U_FAILURE(status)) {
2276 return result;
2277 }
2278
2279 if (parsedLength != tag.size()) {
2280 status = U_ILLEGAL_ARGUMENT_ERROR;
2281 return result;
2282 }
2283
2284 result.init(localeID.data(), /*canonicalize=*/false);
2285 if (result.isBogus()) {
2286 status = U_ILLEGAL_ARGUMENT_ERROR;
2287 }
2288 return result;
2289}
2290
2291void
2292Locale::toLanguageTag(ByteSink& sink, UErrorCode& status) const
2293{
2294 if (U_FAILURE(status)) {
2295 return;
2296 }
2297
2298 if (isBogus()) {
2299 status = U_ILLEGAL_ARGUMENT_ERROR;
2300 return;
2301 }
2302
2303 ulocimp_toLanguageTag(getName(), sink, /*strict=*/false, status);
2304}
2305
2306Locale U_EXPORT2
2307Locale::createFromName (const char *name)
2308{
2309 if (name) {
2310 Locale l("");
2311 l.init(name, false);
2312 return l;
2313 }
2314 else {
2315 return getDefault();
2316 }
2317}
2318
2319Locale U_EXPORT2
2320Locale::createFromName(StringPiece name) {
2321 Locale loc("");
2322 loc.init(name, false);
2323 return loc;
2324}
2325
2326Locale U_EXPORT2
2327Locale::createCanonical(const char* name) {
2328 Locale loc("");
2329 loc.init(name, true);
2330 return loc;
2331}
2332
2333const char *
2334Locale::getISO3Language() const
2335{
2336 return uloc_getISO3Language(getName());
2337}
2338
2339
2340const char *
2341Locale::getISO3Country() const
2342{
2343 return uloc_getISO3Country(getName());
2344}
2345
2346/**
2347 * Return the LCID value as specified in the "LocaleID" resource for this
2348 * locale. The LocaleID must be expressed as a hexadecimal number, from
2349 * one to four digits. If the LocaleID resource is not present, or is
2350 * in an incorrect format, 0 is returned. The LocaleID is for use in
2351 * Windows (it is an LCID), but is available on all platforms.
2352 */
2353uint32_t
2354Locale::getLCID() const
2355{
2356 return uloc_getLCID(getName());
2357}
2358
2359const char* const* U_EXPORT2 Locale::getISOCountries()
2360{
2361 return uloc_getISOCountries();
2362}
2363
2364const char* const* U_EXPORT2 Locale::getISOLanguages()
2365{
2366 return uloc_getISOLanguages();
2367}
2368
2369// Set the locale's data based on a posix id.
2370void Locale::setFromPOSIXID(const char *posixID)
2371{
2372 init(posixID, true);
2373}
2374
2375const Locale & U_EXPORT2
2376Locale::getRoot()
2377{
2378 return getLocale(eROOT);
2379}
2380
2381const Locale & U_EXPORT2
2382Locale::getEnglish()
2383{
2384 return getLocale(eENGLISH);
2385}
2386
2387const Locale & U_EXPORT2
2388Locale::getFrench()
2389{
2390 return getLocale(eFRENCH);
2391}
2392
2393const Locale & U_EXPORT2
2394Locale::getGerman()
2395{
2396 return getLocale(eGERMAN);
2397}
2398
2399const Locale & U_EXPORT2
2400Locale::getItalian()
2401{
2402 return getLocale(eITALIAN);
2403}
2404
2405const Locale & U_EXPORT2
2406Locale::getJapanese()
2407{
2408 return getLocale(eJAPANESE);
2409}
2410
2411const Locale & U_EXPORT2
2412Locale::getKorean()
2413{
2414 return getLocale(eKOREAN);
2415}
2416
2417const Locale & U_EXPORT2
2418Locale::getChinese()
2419{
2420 return getLocale(eCHINESE);
2421}
2422
2423const Locale & U_EXPORT2
2424Locale::getSimplifiedChinese()
2425{
2426 return getLocale(eCHINA);
2427}
2428
2429const Locale & U_EXPORT2
2430Locale::getTraditionalChinese()
2431{
2432 return getLocale(eTAIWAN);
2433}
2434
2435
2436const Locale & U_EXPORT2
2437Locale::getFrance()
2438{
2439 return getLocale(eFRANCE);
2440}
2441
2442const Locale & U_EXPORT2
2443Locale::getGermany()
2444{
2445 return getLocale(eGERMANY);
2446}
2447
2448const Locale & U_EXPORT2
2449Locale::getItaly()
2450{
2451 return getLocale(eITALY);
2452}
2453
2454const Locale & U_EXPORT2
2455Locale::getJapan()
2456{
2457 return getLocale(eJAPAN);
2458}
2459
2460const Locale & U_EXPORT2
2461Locale::getKorea()
2462{
2463 return getLocale(eKOREA);
2464}
2465
2466const Locale & U_EXPORT2
2467Locale::getChina()
2468{
2469 return getLocale(eCHINA);
2470}
2471
2472const Locale & U_EXPORT2
2473Locale::getPRC()
2474{
2475 return getLocale(eCHINA);
2476}
2477
2478const Locale & U_EXPORT2
2479Locale::getTaiwan()
2480{
2481 return getLocale(eTAIWAN);
2482}
2483
2484const Locale & U_EXPORT2
2485Locale::getUK()
2486{
2487 return getLocale(eUK);
2488}
2489
2490const Locale & U_EXPORT2
2491Locale::getUS()
2492{
2493 return getLocale(eUS);
2494}
2495
2496const Locale & U_EXPORT2
2497Locale::getCanada()
2498{
2499 return getLocale(eCANADA);
2500}
2501
2502const Locale & U_EXPORT2
2503Locale::getCanadaFrench()
2504{
2505 return getLocale(eCANADA_FRENCH);
2506}
2507
2508const Locale &
2509Locale::getLocale(int locid)
2510{
2511 Locale *localeCache = getLocaleCache();
2512 U_ASSERT((locid < eMAX_LOCALES)&&(locid>=0))(static_cast <bool> ((locid < eMAX_LOCALES)&&
(locid>=0)) ? void (0) : __assert_fail ("(locid < eMAX_LOCALES)&&(locid>=0)"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
2513 if (localeCache == nullptr) {
2514 // Failure allocating the locale cache.
2515 // The best we can do is return a nullptr reference.
2516 locid = 0;
2517 }
2518 return localeCache[locid]; /*operating on nullptr*/
2519}
2520
2521/*
2522This function is defined this way in order to get around static
2523initialization and static destruction.
2524 */
2525Locale *
2526Locale::getLocaleCache()
2527{
2528 UErrorCode status = U_ZERO_ERROR;
2529 umtx_initOnce(gLocaleCacheInitOnce, locale_init, status);
2530 return gLocaleCache;
2531}
2532
2533class KeywordEnumeration : public StringEnumeration {
2534protected:
2535 FixedString keywords;
2536private:
2537 int32_t length;
2538 const char *current;
2539 static const char fgClassID;
2540
2541public:
2542 static UClassID U_EXPORT2 getStaticClassID() { return (UClassID)&fgClassID; }
2543 virtual UClassID getDynamicClassID() const override { return getStaticClassID(); }
2544public:
2545 KeywordEnumeration(const char *keys, int32_t keywordLen, int32_t currentIndex, UErrorCode &status)
2546 : keywords(), length(keywordLen), current(nullptr) {
2547 if(U_SUCCESS(status) && keywordLen != 0) {
2548 if(keys == nullptr || keywordLen < 0) {
2549 status = U_ILLEGAL_ARGUMENT_ERROR;
2550 } else {
2551 keywords = {keys, static_cast<std::string_view::size_type>(length)};
2552 if (keywords.isEmpty()) {
2553 status = U_MEMORY_ALLOCATION_ERROR;
2554 } else {
2555 current = keywords.data() + currentIndex;
2556 }
2557 }
2558 }
2559 }
2560
2561 virtual ~KeywordEnumeration();
2562
2563 virtual StringEnumeration * clone() const override
2564 {
2565 UErrorCode status = U_ZERO_ERROR;
2566 return new KeywordEnumeration(
2567 keywords.data(), length,
2568 static_cast<int32_t>(current - keywords.data()), status);
2569 }
2570
2571 virtual int32_t count(UErrorCode& status) const override {
2572 if (U_FAILURE(status)) { return 0; }
2573 const char *kw = keywords.data();
2574 int32_t result = 0;
2575 while(*kw) {
2576 result++;
2577 kw += uprv_strlen(kw):: strlen(kw)+1;
2578 }
2579 return result;
2580 }
2581
2582 virtual const char* next(int32_t* resultLength, UErrorCode& status) override {
2583 const char* result;
2584 int32_t len;
2585 if(U_SUCCESS(status) && *current != 0) {
2586 result = current;
2587 len = static_cast<int32_t>(uprv_strlen(current):: strlen(current));
2588 current += len+1;
2589 if(resultLength != nullptr) {
2590 *resultLength = len;
2591 }
2592 } else {
2593 if(resultLength != nullptr) {
2594 *resultLength = 0;
2595 }
2596 result = nullptr;
2597 }
2598 return result;
2599 }
2600
2601 virtual const UnicodeString* snext(UErrorCode& status) override {
2602 if (U_FAILURE(status)) { return nullptr; }
2603 int32_t resultLength = 0;
2604 const char *s = next(&resultLength, status);
2605 return setChars(s, resultLength, status);
2606 }
2607
2608 virtual void reset(UErrorCode& status) override {
2609 if (U_FAILURE(status)) { return; }
2610 current = keywords.data();
2611 }
2612};
2613
2614const char KeywordEnumeration::fgClassID = '\0';
2615
2616// Out-of-line virtual destructor to serve as the "key function".
2617KeywordEnumeration::~KeywordEnumeration() = default;
2618
2619// A wrapper around KeywordEnumeration that calls uloc_toUnicodeLocaleKey() in
2620// the next() method for each keyword before returning it.
2621class UnicodeKeywordEnumeration : public KeywordEnumeration {
2622public:
2623 using KeywordEnumeration::KeywordEnumeration;
2624 virtual ~UnicodeKeywordEnumeration();
2625
2626 virtual const char* next(int32_t* resultLength, UErrorCode& status) override {
2627 const char* legacy_key = KeywordEnumeration::next(nullptr, status);
2628 while (U_SUCCESS(status) && legacy_key != nullptr) {
2629 const char* key = uloc_toUnicodeLocaleKey(legacy_key);
2630 if (key != nullptr) {
2631 if (resultLength != nullptr) {
2632 *resultLength = static_cast<int32_t>(uprv_strlen(key):: strlen(key));
2633 }
2634 return key;
2635 }
2636 // Not a Unicode keyword, could be a t, x or other, continue to look at the next one.
2637 legacy_key = KeywordEnumeration::next(nullptr, status);
2638 }
2639 if (resultLength != nullptr) *resultLength = 0;
2640 return nullptr;
2641 }
2642 virtual int32_t count(UErrorCode& status) const override {
2643 if (U_FAILURE(status)) { return 0; }
2644 const char *kw = keywords.data();
2645 int32_t result = 0;
2646 while(*kw) {
2647 if (uloc_toUnicodeLocaleKey(kw) != nullptr) {
2648 result++;
2649 }
2650 kw += uprv_strlen(kw):: strlen(kw)+1;
2651 }
2652 return result;
2653 }
2654};
2655
2656// Out-of-line virtual destructor to serve as the "key function".
2657UnicodeKeywordEnumeration::~UnicodeKeywordEnumeration() = default;
2658
2659StringEnumeration *
2660Locale::createKeywords(UErrorCode &status) const
2661{
2662 StringEnumeration *result = nullptr;
2663
2664 if (U_FAILURE(status)) {
2665 return result;
2666 }
2667
2668 const char* variantStart = uprv_strchr(getName(), '@'):: strchr(getName(), '@');
2669 const char* assignment = uprv_strchr(getName(), '='):: strchr(getName(), '=');
2670 if(variantStart) {
2671 if(assignment > variantStart) {
2672 CharString keywords = ulocimp_getKeywords(variantStart + 1, '@', false, status);
2673 if (U_SUCCESS(status) && !keywords.isEmpty()) {
2674 result = new KeywordEnumeration(keywords.data(), keywords.length(), 0, status);
2675 if (!result) {
2676 status = U_MEMORY_ALLOCATION_ERROR;
2677 }
2678 }
2679 } else {
2680 status = U_INVALID_FORMAT_ERROR;
2681 }
2682 }
2683 return result;
2684}
2685
2686StringEnumeration *
2687Locale::createUnicodeKeywords(UErrorCode &status) const
2688{
2689 StringEnumeration *result = nullptr;
2690
2691 if (U_FAILURE(status)) {
2692 return result;
2693 }
2694
2695 const char* variantStart = uprv_strchr(getName(), '@'):: strchr(getName(), '@');
2696 const char* assignment = uprv_strchr(getName(), '='):: strchr(getName(), '=');
2697 if(variantStart) {
2698 if(assignment > variantStart) {
2699 CharString keywords = ulocimp_getKeywords(variantStart + 1, '@', false, status);
2700 if (U_SUCCESS(status) && !keywords.isEmpty()) {
2701 result = new UnicodeKeywordEnumeration(keywords.data(), keywords.length(), 0, status);
2702 if (!result) {
2703 status = U_MEMORY_ALLOCATION_ERROR;
2704 }
2705 }
2706 } else {
2707 status = U_INVALID_FORMAT_ERROR;
2708 }
2709 }
2710 return result;
2711}
2712
2713int32_t
2714Locale::getKeywordValue(const char* keywordName, char *buffer, int32_t bufLen, UErrorCode &status) const
2715{
2716 return uloc_getKeywordValue(getName(), keywordName, buffer, bufLen, &status);
2717}
2718
2719void
2720Locale::getKeywordValue(StringPiece keywordName, ByteSink& sink, UErrorCode& status) const {
2721 if (U_FAILURE(status)) {
2722 return;
2723 }
2724
2725 if (isBogus()) {
2726 status = U_ILLEGAL_ARGUMENT_ERROR;
2727 return;
2728 }
2729
2730 ulocimp_getKeywordValue(getName(), keywordName, sink, status);
2731}
2732
2733void
2734Locale::getUnicodeKeywordValue(StringPiece keywordName,
2735 ByteSink& sink,
2736 UErrorCode& status) const {
2737 if (U_FAILURE(status)) {
2738 return;
2739 }
2740
2741 std::optional<std::string_view> legacy_key = ulocimp_toLegacyKeyWithFallback(keywordName);
2742 if (!legacy_key.has_value()) {
2743 status = U_ILLEGAL_ARGUMENT_ERROR;
2744 return;
2745 }
2746
2747 auto legacy_value = getKeywordValue<CharString>(*legacy_key, status);
2748
2749 if (U_FAILURE(status)) {
2750 return;
2751 }
2752
2753 std::optional<std::string_view> unicode_value =
2754 ulocimp_toBcpTypeWithFallback(keywordName, legacy_value.toStringPiece());
2755 if (!unicode_value.has_value()) {
2756 status = U_ILLEGAL_ARGUMENT_ERROR;
2757 return;
2758 }
2759
2760 sink.Append(unicode_value->data(), static_cast<int32_t>(unicode_value->size()));
2761}
2762
2763void
2764Locale::setKeywordValue(StringPiece keywordName,
2765 StringPiece keywordValue,
2766 UErrorCode& status) {
2767 if (U_FAILURE(status)) { return; }
2768 if (keywordName.empty()) {
2769 status = U_ILLEGAL_ARGUMENT_ERROR;
2770 return;
2771 }
2772 if (status == U_STRING_NOT_TERMINATED_WARNING) {
2773 status = U_ZERO_ERROR;
2774 }
2775
2776 CharString localeID(getName(), -1, status);
2777 ulocimp_setKeywordValue(keywordName, keywordValue, localeID, status);
2778 if (U_FAILURE(status)) {
2779 if (status == U_MEMORY_ALLOCATION_ERROR) {
2780 setToBogus();
2781 }
2782 return;
2783 }
2784
2785 const char* at = locale_getKeywordsStart(localeID.toStringPiece());
2786 bool hasKeywords = at != nullptr && uprv_strchr(at + 1, '='):: strchr(at + 1, '=') != nullptr;
2787
2788 Nest* nest = payload.get<Nest>();
2789 if (!hasKeywords) {
2790 if (nest == nullptr) {
2791 // There are no longer any keywords left, so it might now be
2792 // possible to move the payload from Heap to Nest.
2793 Heap* heap = payload.get<Heap>();
2794 U_ASSERT(heap != nullptr)(static_cast <bool> (heap != nullptr) ? void (0) : __assert_fail
("heap != nullptr", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
;
2795 if (Nest::fits(localeID.length(), heap->language, heap->script, heap->region)) {
2796 int32_t variantBegin = heap->ptr->variantBegin;
2797 U_ASSERT(variantBegin >= 0)(static_cast <bool> (variantBegin >= 0) ? void (0) :
__assert_fail ("variantBegin >= 0", __builtin_FILE (), __builtin_LINE
(), __extension__ __PRETTY_FUNCTION__))
;
2798 U_ASSERT(static_cast<size_t>(variantBegin) < sizeof Nest::baseName)(static_cast <bool> (static_cast<size_t>(variantBegin
) < sizeof Nest::baseName) ? void (0) : __assert_fail ("static_cast<size_t>(variantBegin) < sizeof Nest::baseName"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
))
;
2799 nest = &payload.emplace<Nest>(std::move(*heap), static_cast<uint8_t>(variantBegin));
2800 localeID.extract(nest->baseName, sizeof Nest::baseName, status);
2801 } else {
2802 heap->ptr->baseName.clear();
2803 heap->ptr->fullName = localeID.toStringPiece();
2804 if (heap->ptr->fullName.isEmpty()) {
2805 status = U_MEMORY_ALLOCATION_ERROR;
2806 setToBogus();
2807 return;
2808 }
2809 }
2810 }
2811 } else {
2812 Heap* heap = nullptr;
2813 if (nest != nullptr) {
2814 // A keyword has been added, so the payload now needs to be moved
2815 // from Nest to Heap so that it can get a baseName.
2816 Nest copy(*nest);
2817 heap = &payload.emplace<Heap>(copy.language,
2818 copy.script,
2819 copy.region,
2820 copy.variantBegin);
2821 if (isBogus()) {
2822 status = U_MEMORY_ALLOCATION_ERROR;
2823 return;
2824 }
2825 } else {
2826 heap = payload.get<Heap>();
2827 }
2828 U_ASSERT(heap != nullptr)(static_cast <bool> (heap != nullptr) ? void (0) : __assert_fail
("heap != nullptr", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
;
2829 heap->ptr->fullName = localeID.toStringPiece();
2830 if (heap->ptr->fullName.isEmpty()) {
2831 status = U_MEMORY_ALLOCATION_ERROR;
2832 setToBogus();
2833 return;
2834 }
2835
2836 if (heap->ptr->baseName.isEmpty()) {
2837 // Has added the first keyword, meaning that the fullName is no longer also the baseName.
2838 if (std::string_view::size_type baseNameLength = at - localeID.data(); baseNameLength > 0) {
2839 heap->ptr->baseName = {heap->ptr->fullName.data(), baseNameLength};
2840 if (heap->ptr->baseName.isEmpty()) {
2841 status = U_MEMORY_ALLOCATION_ERROR;
2842 setToBogus();
2843 return;
2844 }
2845 }
2846 }
2847 }
2848}
2849
2850void
2851Locale::setUnicodeKeywordValue(StringPiece keywordName,
2852 StringPiece keywordValue,
2853 UErrorCode& status) {
2854 if (U_FAILURE(status)) {
2855 return;
2856 }
2857
2858 std::optional<std::string_view> legacy_key = ulocimp_toLegacyKeyWithFallback(keywordName);
2859 if (!legacy_key.has_value()) {
2860 status = U_ILLEGAL_ARGUMENT_ERROR;
2861 return;
2862 }
2863
2864 std::string_view value;
2865
2866 if (!keywordValue.empty()) {
2867 std::optional<std::string_view> legacy_value =
2868 ulocimp_toLegacyTypeWithFallback(keywordName, keywordValue);
2869 if (!legacy_value.has_value()) {
2870 status = U_ILLEGAL_ARGUMENT_ERROR;
2871 return;
2872 }
2873 value = *legacy_value;
2874 }
2875
2876 setKeywordValue(*legacy_key, value, status);
2877}
2878
2879const char*
2880Locale::getCountry() const {
2881 return getField<&Nest::getRegion, &Heap::getRegion>();
2882}
2883
2884const char*
2885Locale::getLanguage() const {
2886 return getField<&Nest::getLanguage, &Heap::getLanguage>();
2887}
2888
2889const char*
2890Locale::getScript() const {
2891 return getField<&Nest::getScript, &Heap::getScript>();
2892}
2893
2894const char*
2895Locale::getVariant() const {
2896 return getField<&Nest::getVariant, &Heap::getVariant>();
2897}
2898
2899const char*
2900Locale::getName() const {
2901 return getField<&Nest::getBaseName, &Heap::getFullName>();
2902}
2903
2904const char*
2905Locale::getBaseName() const {
2906 return getField<&Nest::getBaseName, &Heap::getBaseName>();
2907}
2908
2909template <const char* (Locale::Nest::*const NEST)() const,
2910 const char* (Locale::Heap::*const HEAP)() const>
2911const char* Locale::getField() const {
2912 return payload.visit([] { return ""; },
2913 [](const Nest& nest) { return (nest.*NEST)(); },
2914 [](const Heap& heap) { return (heap.*HEAP)(); });
2915}
2916
2917Locale::Iterator::~Iterator() = default;
2918
2919//eof
2920U_NAMESPACE_END}

/root/firefox-clang/intl/icu/source/common/cmemory.h

1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/*
4******************************************************************************
5*
6* Copyright (C) 1997-2016, International Business Machines
7* Corporation and others. All Rights Reserved.
8*
9******************************************************************************
10*
11* File CMEMORY.H
12*
13* Contains stdlib.h/string.h memory functions
14*
15* @author Bertrand A. Damiba
16*
17* Modification History:
18*
19* Date Name Description
20* 6/20/98 Bertrand Created.
21* 05/03/99 stephen Changed from functions to macros.
22*
23******************************************************************************
24*/
25
26#ifndef CMEMORY_H
27#define CMEMORY_H
28
29#include "unicode/utypes.h"
30
31#include <stddef.h>
32#include <string.h>
33#include "unicode/localpointer.h"
34#include "uassert.h"
35
36#if U_DEBUG1 && defined(UPRV_MALLOC_COUNT)
37#include <stdio.h>
38#endif
39
40// uprv_memcpy and uprv_memmove
41#if defined(__clang__1)
42#define uprv_memcpy(dst, src, size)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (src != __null
) ? void (0) : __assert_fail ("src != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(dst, src, size); } while (false)
UPRV_BLOCK_MACRO_BEGINdo { \
43 /* Suppress warnings about addresses that will never be NULL */ \
44 _Pragma("clang diagnostic push")clang diagnostic push \
45 _Pragma("clang diagnostic ignored \"-Waddress\"")clang diagnostic ignored "-Waddress" \
46 U_ASSERT(dst != NULL)(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
47 U_ASSERT(src != NULL)(static_cast <bool> (src != __null) ? void (0) : __assert_fail
("src != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
48 _Pragma("clang diagnostic pop")clang diagnostic pop \
49 U_STANDARD_CPP_NAMESPACE:: memcpy(dst, src, size); \
50} UPRV_BLOCK_MACRO_ENDwhile (false)
51#define uprv_memmove(dst, src, size)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (src != __null
) ? void (0) : __assert_fail ("src != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memmove(dst, src, size); } while (false)
UPRV_BLOCK_MACRO_BEGINdo { \
52 /* Suppress warnings about addresses that will never be NULL */ \
53 _Pragma("clang diagnostic push")clang diagnostic push \
54 _Pragma("clang diagnostic ignored \"-Waddress\"")clang diagnostic ignored "-Waddress" \
55 U_ASSERT(dst != NULL)(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
56 U_ASSERT(src != NULL)(static_cast <bool> (src != __null) ? void (0) : __assert_fail
("src != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
57 _Pragma("clang diagnostic pop")clang diagnostic pop \
58 U_STANDARD_CPP_NAMESPACE:: memmove(dst, src, size); \
59} UPRV_BLOCK_MACRO_ENDwhile (false)
60#elif defined(__GNUC__4)
61#define uprv_memcpy(dst, src, size)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (src != __null
) ? void (0) : __assert_fail ("src != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(dst, src, size); } while (false)
UPRV_BLOCK_MACRO_BEGINdo { \
62 /* Suppress warnings about addresses that will never be NULL */ \
63 _Pragma("GCC diagnostic push")GCC diagnostic push \
64 _Pragma("GCC diagnostic ignored \"-Waddress\"")GCC diagnostic ignored "-Waddress" \
65 U_ASSERT(dst != NULL)(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
66 U_ASSERT(src != NULL)(static_cast <bool> (src != __null) ? void (0) : __assert_fail
("src != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
67 _Pragma("GCC diagnostic pop")GCC diagnostic pop \
68 U_STANDARD_CPP_NAMESPACE:: memcpy(dst, src, size); \
69} UPRV_BLOCK_MACRO_ENDwhile (false)
70#define uprv_memmove(dst, src, size)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (src != __null
) ? void (0) : __assert_fail ("src != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memmove(dst, src, size); } while (false)
UPRV_BLOCK_MACRO_BEGINdo { \
71 /* Suppress warnings about addresses that will never be NULL */ \
72 _Pragma("GCC diagnostic push")GCC diagnostic push \
73 _Pragma("GCC diagnostic ignored \"-Waddress\"")GCC diagnostic ignored "-Waddress" \
74 U_ASSERT(dst != NULL)(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
75 U_ASSERT(src != NULL)(static_cast <bool> (src != __null) ? void (0) : __assert_fail
("src != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
76 _Pragma("GCC diagnostic pop")GCC diagnostic pop \
77 U_STANDARD_CPP_NAMESPACE:: memmove(dst, src, size); \
78} UPRV_BLOCK_MACRO_ENDwhile (false)
79#else
80#define uprv_memcpy(dst, src, size)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (src != __null
) ? void (0) : __assert_fail ("src != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(dst, src, size); } while (false)
UPRV_BLOCK_MACRO_BEGINdo { \
81 U_ASSERT(dst != NULL)(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
82 U_ASSERT(src != NULL)(static_cast <bool> (src != __null) ? void (0) : __assert_fail
("src != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
83 U_STANDARD_CPP_NAMESPACE:: memcpy(dst, src, size); \
84} UPRV_BLOCK_MACRO_ENDwhile (false)
85#define uprv_memmove(dst, src, size)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (src != __null
) ? void (0) : __assert_fail ("src != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memmove(dst, src, size); } while (false)
UPRV_BLOCK_MACRO_BEGINdo { \
86 U_ASSERT(dst != NULL)(static_cast <bool> (dst != __null) ? void (0) : __assert_fail
("dst != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
87 U_ASSERT(src != NULL)(static_cast <bool> (src != __null) ? void (0) : __assert_fail
("src != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__))
; \
88 U_STANDARD_CPP_NAMESPACE:: memmove(dst, src, size); \
89} UPRV_BLOCK_MACRO_ENDwhile (false)
90#endif
91
92/**
93 * \def UPRV_LENGTHOF
94 * Convenience macro to determine the length of a fixed array at compile-time.
95 * @param array A fixed length array
96 * @return The length of the array, in elements
97 * @internal
98 */
99#define UPRV_LENGTHOF(array)(int32_t)(sizeof(array)/sizeof((array)[0])) (int32_t)(sizeof(array)/sizeof((array)[0]))
100#define uprv_memset(buffer, mark, size):: memset(buffer, mark, size) U_STANDARD_CPP_NAMESPACE:: memset(buffer, mark, size)
101#define uprv_memcmp(buffer1, buffer2, size):: memcmp(buffer1, buffer2,size) U_STANDARD_CPP_NAMESPACE:: memcmp(buffer1, buffer2,size)
102#define uprv_memchr(ptr, value, num):: memchr(ptr, value, num) U_STANDARD_CPP_NAMESPACE:: memchr(ptr, value, num)
103
104U_CAPIextern "C" void * U_EXPORT2
105uprv_malloc(size_t s) U_MALLOC_ATTR__attribute__ ((__malloc__)) U_ALLOC_SIZE_ATTR(1)__attribute__ ((alloc_size(1)));
106
107U_CAPIextern "C" void * U_EXPORT2
108uprv_realloc(void *mem, size_t size) U_ALLOC_SIZE_ATTR(2)__attribute__ ((alloc_size(2)));
109
110U_CAPIextern "C" void U_EXPORT2
111uprv_free(void *mem);
112
113U_CAPIextern "C" void * U_EXPORT2
114uprv_calloc(size_t num, size_t size) U_MALLOC_ATTR__attribute__ ((__malloc__)) U_ALLOC_SIZE_ATTR2(1,2)__attribute__ ((alloc_size(1,2)));
115
116/**
117 * Get the least significant bits of a pointer (a memory address).
118 * For example, with a mask of 3, the macro gets the 2 least significant bits,
119 * which will be 0 if the pointer is 32-bit (4-byte) aligned.
120 *
121 * uintptr_t is the most appropriate integer type to cast to.
122 */
123#define U_POINTER_MASK_LSB(ptr, mask)((uintptr_t)(ptr) & (mask)) ((uintptr_t)(ptr) & (mask))
124
125/**
126 * Create & return an instance of "type" in statically allocated storage.
127 * e.g.
128 * static std::mutex *myMutex = STATIC_NEW(std::mutex);
129 * To destroy an object created in this way, invoke the destructor explicitly, e.g.
130 * myMutex->~mutex();
131 * DO NOT use delete.
132 * DO NOT use with class UMutex, which has specific support for static instances.
133 *
134 * STATIC_NEW is intended for use when
135 * - We want a static (or global) object.
136 * - We don't want it to ever be destructed, or to explicitly control destruction,
137 * to avoid use-after-destruction problems.
138 * - We want to avoid an ordinary heap allocated object,
139 * to avoid the possibility of memory allocation failures, and
140 * to avoid memory leak reports, from valgrind, for example.
141 * This is defined as a macro rather than a template function because each invocation
142 * must define distinct static storage for the object being returned.
143 */
144#define STATIC_NEW(type)[] () { alignas(type) static char storage[sizeof(type)]; return
new(storage) type();} ()
[] () { \
145 alignas(type) static char storage[sizeof(type)]; \
146 return new(storage) type();} ()
147
148/**
149 * Heap clean up function, called from u_cleanup()
150 * Clears any user heap functions from u_setMemoryFunctions()
151 * Does NOT deallocate any remaining allocated memory.
152 */
153U_CFUNCextern "C" UBool
154cmemory_cleanup(void);
155
156/**
157 * A function called by <TT>uhash_remove</TT>,
158 * <TT>uhash_close</TT>, or <TT>uhash_put</TT> to delete
159 * an existing key or value.
160 * @param obj A key or value stored in a hashtable
161 * @see uprv_deleteUObject
162 */
163typedef void U_CALLCONV UObjectDeleter(void* obj);
164
165/**
166 * Deleter for UObject instances.
167 * Works for all subclasses of UObject because it has a virtual destructor.
168 */
169U_CAPIextern "C" void U_EXPORT2
170uprv_deleteUObject(void *obj);
171
172#ifdef __cplusplus202002L
173
174#include <utility>
175#include "unicode/uobject.h"
176
177U_NAMESPACE_BEGINnamespace icu_78 {
178
179/**
180 * "Smart pointer" class, deletes memory via uprv_free().
181 * For most methods see the LocalPointerBase base class.
182 * Adds operator[] for array item access.
183 *
184 * @see LocalPointerBase
185 */
186template<typename T>
187class LocalMemory : public LocalPointerBase<T> {
188public:
189 using LocalPointerBase<T>::operator*;
190 using LocalPointerBase<T>::operator->;
191 /**
192 * Constructor takes ownership.
193 * @param p simple pointer to an array of T items that is adopted
194 */
195 explicit LocalMemory(T *p=nullptr) : LocalPointerBase<T>(p) {}
5
Passing null pointer value via 1st parameter 'p'
6
Calling default constructor for 'LocalPointerBase<const char *>'
8
Returning from default constructor for 'LocalPointerBase<const char *>'
196 /**
197 * Move constructor, leaves src with isNull().
198 * @param src source smart pointer
199 */
200 LocalMemory(LocalMemory<T> &&src) noexcept : LocalPointerBase<T>(src.ptr) {
201 src.ptr=nullptr;
202 }
203 /**
204 * Destructor deletes the memory it owns.
205 */
206 ~LocalMemory() {
207 uprv_free(LocalPointerBase<T>::ptr);
208 }
209 /**
210 * Move assignment operator, leaves src with isNull().
211 * The behavior is undefined if *this and src are the same object.
212 * @param src source smart pointer
213 * @return *this
214 */
215 LocalMemory<T> &operator=(LocalMemory<T> &&src) noexcept {
216 uprv_free(LocalPointerBase<T>::ptr);
217 LocalPointerBase<T>::ptr=src.ptr;
218 src.ptr=nullptr;
219 return *this;
220 }
221 /**
222 * Swap pointers.
223 * @param other other smart pointer
224 */
225 void swap(LocalMemory<T> &other) noexcept {
226 T *temp=LocalPointerBase<T>::ptr;
227 LocalPointerBase<T>::ptr=other.ptr;
228 other.ptr=temp;
229 }
230 /**
231 * Non-member LocalMemory swap function.
232 * @param p1 will get p2's pointer
233 * @param p2 will get p1's pointer
234 */
235 friend inline void swap(LocalMemory<T> &p1, LocalMemory<T> &p2) noexcept {
236 p1.swap(p2);
237 }
238 /**
239 * Deletes the array it owns,
240 * and adopts (takes ownership of) the one passed in.
241 * @param p simple pointer to an array of T items that is adopted
242 */
243 void adoptInstead(T *p) {
244 uprv_free(LocalPointerBase<T>::ptr);
245 LocalPointerBase<T>::ptr=p;
246 }
247 /**
248 * Deletes the array it owns, allocates a new one and reset its bytes to 0.
249 * Returns the new array pointer.
250 * If the allocation fails, then the current array is unchanged and
251 * this method returns nullptr.
252 * @param newCapacity must be >0
253 * @return the allocated array pointer, or nullptr if the allocation failed
254 */
255 inline T *allocateInsteadAndReset(int32_t newCapacity=1);
256 /**
257 * Deletes the array it owns and allocates a new one, copying length T items.
258 * Returns the new array pointer.
259 * If the allocation fails, then the current array is unchanged and
260 * this method returns nullptr.
261 * @param newCapacity must be >0
262 * @param length number of T items to be copied from the old array to the new one;
263 * must be no more than the capacity of the old array,
264 * which the caller must track because the LocalMemory does not track it
265 * @return the allocated array pointer, or nullptr if the allocation failed
266 */
267 inline T *allocateInsteadAndCopy(int32_t newCapacity=1, int32_t length=0);
268 /**
269 * Array item access (writable).
270 * No index bounds check.
271 * @param i array index
272 * @return reference to the array item
273 */
274 T &operator[](ptrdiff_t i) const { return LocalPointerBase<T>::ptr[i]; }
21
Returning null reference
275};
276
277template<typename T>
278inline T *LocalMemory<T>::allocateInsteadAndReset(int32_t newCapacity) {
279 if(newCapacity>0) {
280 T *p=(T *)uprv_malloc(newCapacity*sizeof(T));
281 if(p!=nullptr) {
282 uprv_memset(p, 0, newCapacity*sizeof(T)):: memset(p, 0, newCapacity*sizeof(T));
283 uprv_free(LocalPointerBase<T>::ptr);
284 LocalPointerBase<T>::ptr=p;
285 }
286 return p;
287 } else {
288 return nullptr;
289 }
290}
291
292
293template<typename T>
294inline T *LocalMemory<T>::allocateInsteadAndCopy(int32_t newCapacity, int32_t length) {
295 if(newCapacity>0) {
296 T *p=(T *)uprv_malloc(newCapacity*sizeof(T));
297 if(p!=nullptr) {
298 if(length>0) {
299 if(length>newCapacity) {
300 length=newCapacity;
301 }
302 uprv_memcpy(p, LocalPointerBase<T>::ptr, (size_t)length*sizeof(T))do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (p != __null) ? void (0) : __assert_fail
("p != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (LocalPointerBase
<T>::ptr != __null) ? void (0) : __assert_fail ("LocalPointerBase<T>::ptr != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); clang diagnostic pop :: memcpy(p, LocalPointerBase<T>
::ptr, (size_t)length*sizeof(T)); } while (false)
;
303 }
304 uprv_free(LocalPointerBase<T>::ptr);
305 LocalPointerBase<T>::ptr=p;
306 }
307 return p;
308 } else {
309 return nullptr;
310 }
311}
312
313/**
314 * Simple array/buffer management class using uprv_malloc() and uprv_free().
315 * Provides an internal array with fixed capacity. Can alias another array
316 * or allocate one.
317 *
318 * The array address is properly aligned for type T. It might not be properly
319 * aligned for types larger than T (or larger than the largest subtype of T).
320 *
321 * Unlike LocalMemory and LocalArray, this class never adopts
322 * (takes ownership of) another array.
323 *
324 * WARNING: MaybeStackArray only works with primitive (plain-old data) types.
325 * It does NOT know how to call a destructor! If you work with classes with
326 * destructors, consider:
327 *
328 * - LocalArray in localpointer.h if you know the length ahead of time
329 * - MaybeStackVector if you know the length at runtime
330 */
331template<typename T, int32_t stackCapacity>
332class MaybeStackArray {
333public:
334 // No heap allocation. Use only on the stack.
335 static void* U_EXPORT2 operator new(size_t) noexcept = delete;
336 static void* U_EXPORT2 operator new[](size_t) noexcept = delete;
337 static void* U_EXPORT2 operator new(size_t, void*) noexcept = delete;
338
339 /**
340 * Default constructor initializes with internal T[stackCapacity] buffer.
341 */
342 MaybeStackArray() : ptr(stackArray), capacity(stackCapacity), needToRelease(false) {}
343 /**
344 * Automatically allocates the heap array if the argument is larger than the stack capacity.
345 * Intended for use when an approximate capacity is known at compile time but the true
346 * capacity is not known until runtime.
347 */
348 MaybeStackArray(int32_t newCapacity, UErrorCode status) : MaybeStackArray() {
349 if (U_FAILURE(status)) {
350 return;
351 }
352 if (capacity < newCapacity) {
353 if (resize(newCapacity) == nullptr) {
354 status = U_MEMORY_ALLOCATION_ERROR;
355 }
356 }
357 }
358 /**
359 * Destructor deletes the array (if owned).
360 */
361 ~MaybeStackArray() { releaseArray(); }
362 /**
363 * Move constructor: transfers ownership or copies the stack array.
364 */
365 MaybeStackArray(MaybeStackArray<T, stackCapacity> &&src) noexcept;
366 /**
367 * Move assignment: transfers ownership or copies the stack array.
368 */
369 MaybeStackArray<T, stackCapacity> &operator=(MaybeStackArray<T, stackCapacity> &&src) noexcept;
370 /**
371 * Returns the array capacity (number of T items).
372 * @return array capacity
373 */
374 int32_t getCapacity() const { return capacity; }
375 /**
376 * Access without ownership change.
377 * @return the array pointer
378 */
379 T *getAlias() const { return ptr; }
380 /**
381 * Returns the array limit. Simple convenience method.
382 * @return getAlias()+getCapacity()
383 */
384 T *getArrayLimit() const { return getAlias()+capacity; }
385 // No "operator T *() const" because that can make
386 // expressions like mbs[index] ambiguous for some compilers.
387 /**
388 * Array item access (const).
389 * No index bounds check.
390 * @param i array index
391 * @return reference to the array item
392 */
393 const T &operator[](ptrdiff_t i) const { return ptr[i]; }
394 /**
395 * Array item access (writable).
396 * No index bounds check.
397 * @param i array index
398 * @return reference to the array item
399 */
400 T &operator[](ptrdiff_t i) { return ptr[i]; }
401 /**
402 * Deletes the array (if owned) and aliases another one, no transfer of ownership.
403 * If the arguments are illegal, then the current array is unchanged.
404 * @param otherArray must not be nullptr
405 * @param otherCapacity must be >0
406 */
407 void aliasInstead(T *otherArray, int32_t otherCapacity) {
408 if(otherArray!=nullptr && otherCapacity>0) {
409 releaseArray();
410 ptr=otherArray;
411 capacity=otherCapacity;
412 needToRelease=false;
413 }
414 }
415 /**
416 * Deletes the array (if owned) and allocates a new one, copying length T items.
417 * Returns the new array pointer.
418 * If the allocation fails, then the current array is unchanged and
419 * this method returns nullptr.
420 * @param newCapacity can be less than or greater than the current capacity;
421 * must be >0
422 * @param length number of T items to be copied from the old array to the new one
423 * @return the allocated array pointer, or nullptr if the allocation failed
424 */
425 inline T *resize(int32_t newCapacity, int32_t length=0);
426 /**
427 * Gives up ownership of the array if owned, or else clones it,
428 * copying length T items; resets itself to the internal stack array.
429 * Returns nullptr if the allocation failed.
430 * @param length number of T items to copy when cloning,
431 * and capacity of the clone when cloning
432 * @param resultCapacity will be set to the returned array's capacity (output-only)
433 * @return the array pointer;
434 * caller becomes responsible for deleting the array
435 */
436 inline T *orphanOrClone(int32_t length, int32_t &resultCapacity);
437
438protected:
439 // Resizes the array to the size of src, then copies the contents of src.
440 void copyFrom(const MaybeStackArray &src, UErrorCode &status) {
441 if (U_FAILURE(status)) {
442 return;
443 }
444 if (this->resize(src.capacity, 0) == nullptr) {
445 status = U_MEMORY_ALLOCATION_ERROR;
446 return;
447 }
448 uprv_memcpy(this->ptr, src.ptr, (size_t)capacity * sizeof(T))do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (this->ptr != __null) ? void (
0) : __assert_fail ("this->ptr != __null", __builtin_FILE (
), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); (static_cast
<bool> (src.ptr != __null) ? void (0) : __assert_fail (
"src.ptr != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); clang diagnostic pop :: memcpy(this->
ptr, src.ptr, (size_t)capacity * sizeof(T)); } while (false)
;
449 }
450
451private:
452 T *ptr;
453 int32_t capacity;
454 UBool needToRelease;
455 T stackArray[stackCapacity];
456 void releaseArray() {
457 if(needToRelease) {
458 uprv_free(ptr);
459 }
460 }
461 void resetToStackArray() {
462 ptr=stackArray;
463 capacity=stackCapacity;
464 needToRelease=false;
465 }
466 /* No comparison operators with other MaybeStackArray's. */
467 bool operator==(const MaybeStackArray & /*other*/) = delete;
468 bool operator!=(const MaybeStackArray & /*other*/) = delete;
469 /* No ownership transfer: No copy constructor, no assignment operator. */
470 MaybeStackArray(const MaybeStackArray & /*other*/) = delete;
471 void operator=(const MaybeStackArray & /*other*/) = delete;
472};
473
474template<typename T, int32_t stackCapacity>
475icu::MaybeStackArray<T, stackCapacity>::MaybeStackArray(
476 MaybeStackArray <T, stackCapacity>&& src) noexcept
477 : ptr(src.ptr), capacity(src.capacity), needToRelease(src.needToRelease) {
478 if (src.ptr == src.stackArray) {
479 ptr = stackArray;
480 uprv_memcpy(stackArray, src.stackArray, sizeof(T) * src.capacity)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (stackArray != __null) ? void (0)
: __assert_fail ("stackArray != __null", __builtin_FILE (), __builtin_LINE
(), __extension__ __PRETTY_FUNCTION__)); (static_cast <bool
> (src.stackArray != __null) ? void (0) : __assert_fail ("src.stackArray != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); clang diagnostic pop :: memcpy(stackArray, src.stackArray
, sizeof(T) * src.capacity); } while (false)
;
481 } else {
482 src.resetToStackArray(); // take ownership away from src
483 }
484}
485
486template<typename T, int32_t stackCapacity>
487inline MaybeStackArray <T, stackCapacity>&
488MaybeStackArray<T, stackCapacity>::operator=(MaybeStackArray <T, stackCapacity>&& src) noexcept {
489 releaseArray(); // in case this instance had its own memory allocated
490 capacity = src.capacity;
491 needToRelease = src.needToRelease;
492 if (src.ptr == src.stackArray) {
493 ptr = stackArray;
494 uprv_memcpy(stackArray, src.stackArray, sizeof(T) * src.capacity)do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (stackArray != __null) ? void (0)
: __assert_fail ("stackArray != __null", __builtin_FILE (), __builtin_LINE
(), __extension__ __PRETTY_FUNCTION__)); (static_cast <bool
> (src.stackArray != __null) ? void (0) : __assert_fail ("src.stackArray != __null"
, __builtin_FILE (), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__
)); clang diagnostic pop :: memcpy(stackArray, src.stackArray
, sizeof(T) * src.capacity); } while (false)
;
495 } else {
496 ptr = src.ptr;
497 src.resetToStackArray(); // take ownership away from src
498 }
499 return *this;
500}
501
502template<typename T, int32_t stackCapacity>
503inline T *MaybeStackArray<T, stackCapacity>::resize(int32_t newCapacity, int32_t length) {
504 if(newCapacity>0) {
505#if U_DEBUG1 && defined(UPRV_MALLOC_COUNT)
506 ::fprintf(::stderrstderr, "MaybeStackArray (resize) alloc %d * %lu\n", newCapacity, sizeof(T));
507#endif
508 T *p=(T *)uprv_malloc(newCapacity*sizeof(T));
509 if(p!=nullptr) {
510 if(length>0) {
511 if(length>capacity) {
512 length=capacity;
513 }
514 if(length>newCapacity) {
515 length=newCapacity;
516 }
517 uprv_memcpy(p, ptr, (size_t)length*sizeof(T))do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (p != __null) ? void (0) : __assert_fail
("p != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (ptr != __null
) ? void (0) : __assert_fail ("ptr != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(p, ptr, (size_t)length*sizeof(T));
} while (false)
;
518 }
519 releaseArray();
520 ptr=p;
521 capacity=newCapacity;
522 needToRelease=true;
523 }
524 return p;
525 } else {
526 return nullptr;
527 }
528}
529
530template<typename T, int32_t stackCapacity>
531inline T *MaybeStackArray<T, stackCapacity>::orphanOrClone(int32_t length, int32_t &resultCapacity) {
532 T *p;
533 if(needToRelease) {
534 p=ptr;
535 } else if(length<=0) {
536 return nullptr;
537 } else {
538 if(length>capacity) {
539 length=capacity;
540 }
541 p=(T *)uprv_malloc(length*sizeof(T));
542#if U_DEBUG1 && defined(UPRV_MALLOC_COUNT)
543 ::fprintf(::stderrstderr,"MaybeStacArray (orphan) alloc %d * %lu\n", length,sizeof(T));
544#endif
545 if(p==nullptr) {
546 return nullptr;
547 }
548 uprv_memcpy(p, ptr, (size_t)length*sizeof(T))do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (p != __null) ? void (0) : __assert_fail
("p != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (ptr != __null
) ? void (0) : __assert_fail ("ptr != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(p, ptr, (size_t)length*sizeof(T));
} while (false)
;
549 }
550 resultCapacity=length;
551 resetToStackArray();
552 return p;
553}
554
555/**
556 * Variant of MaybeStackArray that allocates a header struct and an array
557 * in one contiguous memory block, using uprv_malloc() and uprv_free().
558 * Provides internal memory with fixed array capacity. Can alias another memory
559 * block or allocate one.
560 * The stackCapacity is the number of T items in the internal memory,
561 * not counting the H header.
562 * Unlike LocalMemory and LocalArray, this class never adopts
563 * (takes ownership of) another memory block.
564 */
565template<typename H, typename T, int32_t stackCapacity>
566class MaybeStackHeaderAndArray {
567public:
568 // No heap allocation. Use only on the stack.
569 static void* U_EXPORT2 operator new(size_t) noexcept = delete;
570 static void* U_EXPORT2 operator new[](size_t) noexcept = delete;
571 static void* U_EXPORT2 operator new(size_t, void*) noexcept = delete;
572
573 /**
574 * Default constructor initializes with internal H+T[stackCapacity] buffer.
575 */
576 MaybeStackHeaderAndArray() : ptr(&stackHeader), capacity(stackCapacity), needToRelease(false) {}
577 /**
578 * Destructor deletes the memory (if owned).
579 */
580 ~MaybeStackHeaderAndArray() { releaseMemory(); }
581 /**
582 * Returns the array capacity (number of T items).
583 * @return array capacity
584 */
585 int32_t getCapacity() const { return capacity; }
586 /**
587 * Access without ownership change.
588 * @return the header pointer
589 */
590 H *getAlias() const { return ptr; }
591 /**
592 * Returns the array start.
593 * @return array start, same address as getAlias()+1
594 */
595 T *getArrayStart() const { return reinterpret_cast<T *>(getAlias()+1); }
596 /**
597 * Returns the array limit.
598 * @return array limit
599 */
600 T *getArrayLimit() const { return getArrayStart()+capacity; }
601 /**
602 * Access without ownership change. Same as getAlias().
603 * A class instance can be used directly in expressions that take a T *.
604 * @return the header pointer
605 */
606 operator H *() const { return ptr; }
607 /**
608 * Array item access (writable).
609 * No index bounds check.
610 * @param i array index
611 * @return reference to the array item
612 */
613 T &operator[](ptrdiff_t i) { return getArrayStart()[i]; }
614 /**
615 * Deletes the memory block (if owned) and aliases another one, no transfer of ownership.
616 * If the arguments are illegal, then the current memory is unchanged.
617 * @param otherArray must not be nullptr
618 * @param otherCapacity must be >0
619 */
620 void aliasInstead(H *otherMemory, int32_t otherCapacity) {
621 if(otherMemory!=nullptr && otherCapacity>0) {
622 releaseMemory();
623 ptr=otherMemory;
624 capacity=otherCapacity;
625 needToRelease=false;
626 }
627 }
628 /**
629 * Deletes the memory block (if owned) and allocates a new one,
630 * copying the header and length T array items.
631 * Returns the new header pointer.
632 * If the allocation fails, then the current memory is unchanged and
633 * this method returns nullptr.
634 * @param newCapacity can be less than or greater than the current capacity;
635 * must be >0
636 * @param length number of T items to be copied from the old array to the new one
637 * @return the allocated pointer, or nullptr if the allocation failed
638 */
639 inline H *resize(int32_t newCapacity, int32_t length=0);
640 /**
641 * Gives up ownership of the memory if owned, or else clones it,
642 * copying the header and length T array items; resets itself to the internal memory.
643 * Returns nullptr if the allocation failed.
644 * @param length number of T items to copy when cloning,
645 * and array capacity of the clone when cloning
646 * @param resultCapacity will be set to the returned array's capacity (output-only)
647 * @return the header pointer;
648 * caller becomes responsible for deleting the array
649 */
650 inline H *orphanOrClone(int32_t length, int32_t &resultCapacity);
651private:
652 H *ptr;
653 int32_t capacity;
654 UBool needToRelease;
655 // stackHeader must precede stackArray immediately.
656 H stackHeader;
657 T stackArray[stackCapacity];
658 void releaseMemory() {
659 if(needToRelease) {
660 uprv_free(ptr);
661 }
662 }
663 /* No comparison operators with other MaybeStackHeaderAndArray's. */
664 bool operator==(const MaybeStackHeaderAndArray & /*other*/) {return false;}
665 bool operator!=(const MaybeStackHeaderAndArray & /*other*/) {return true;}
666 /* No ownership transfer: No copy constructor, no assignment operator. */
667 MaybeStackHeaderAndArray(const MaybeStackHeaderAndArray & /*other*/) {}
668 void operator=(const MaybeStackHeaderAndArray & /*other*/) {}
669};
670
671template<typename H, typename T, int32_t stackCapacity>
672inline H *MaybeStackHeaderAndArray<H, T, stackCapacity>::resize(int32_t newCapacity,
673 int32_t length) {
674 if(newCapacity>=0) {
675#if U_DEBUG1 && defined(UPRV_MALLOC_COUNT)
676 ::fprintf(::stderrstderr,"MaybeStackHeaderAndArray alloc %d + %d * %ul\n", sizeof(H),newCapacity,sizeof(T));
677#endif
678 H *p=(H *)uprv_malloc(sizeof(H)+newCapacity*sizeof(T));
679 if(p!=nullptr) {
680 if(length<0) {
681 length=0;
682 } else if(length>0) {
683 if(length>capacity) {
684 length=capacity;
685 }
686 if(length>newCapacity) {
687 length=newCapacity;
688 }
689 }
690 uprv_memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof(T))do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (p != __null) ? void (0) : __assert_fail
("p != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (ptr != __null
) ? void (0) : __assert_fail ("ptr != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof
(T)); } while (false)
;
691 releaseMemory();
692 ptr=p;
693 capacity=newCapacity;
694 needToRelease=true;
695 }
696 return p;
697 } else {
698 return nullptr;
699 }
700}
701
702template<typename H, typename T, int32_t stackCapacity>
703inline H *MaybeStackHeaderAndArray<H, T, stackCapacity>::orphanOrClone(int32_t length,
704 int32_t &resultCapacity) {
705 H *p;
706 if(needToRelease) {
707 p=ptr;
708 } else {
709 if(length<0) {
710 length=0;
711 } else if(length>capacity) {
712 length=capacity;
713 }
714#if U_DEBUG1 && defined(UPRV_MALLOC_COUNT)
715 ::fprintf(::stderrstderr,"MaybeStackHeaderAndArray (orphan) alloc %ul + %d * %lu\n", sizeof(H),length,sizeof(T));
716#endif
717 p=(H *)uprv_malloc(sizeof(H)+length*sizeof(T));
718 if(p==nullptr) {
719 return nullptr;
720 }
721 uprv_memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof(T))do { clang diagnostic push clang diagnostic ignored "-Waddress"
(static_cast <bool> (p != __null) ? void (0) : __assert_fail
("p != __null", __builtin_FILE (), __builtin_LINE (), __extension__
__PRETTY_FUNCTION__)); (static_cast <bool> (ptr != __null
) ? void (0) : __assert_fail ("ptr != __null", __builtin_FILE
(), __builtin_LINE (), __extension__ __PRETTY_FUNCTION__)); clang
diagnostic pop :: memcpy(p, ptr, sizeof(H)+(size_t)length*sizeof
(T)); } while (false)
;
722 }
723 resultCapacity=length;
724 ptr=&stackHeader;
725 capacity=stackCapacity;
726 needToRelease=false;
727 return p;
728}
729
730/**
731 * A simple memory management class that creates new heap allocated objects (of
732 * any class that has a public constructor), keeps track of them and eventually
733 * deletes them all in its own destructor.
734 *
735 * A typical use-case would be code like this:
736 *
737 * MemoryPool<MyType> pool;
738 *
739 * MyType* o1 = pool.create();
740 * if (o1 != nullptr) {
741 * foo(o1);
742 * }
743 *
744 * MyType* o2 = pool.create(1, 2, 3);
745 * if (o2 != nullptr) {
746 * bar(o2);
747 * }
748 *
749 * // MemoryPool will take care of deleting the MyType objects.
750 *
751 * It doesn't do anything more than that, and is intentionally kept minimalist.
752 */
753template<typename T, int32_t stackCapacity = 8>
754class MemoryPool : public UMemory {
755public:
756 MemoryPool() : fCount(0), fPool() {}
757
758 ~MemoryPool() {
759 for (int32_t i = 0; i < fCount; ++i) {
760 delete fPool[i];
761 }
762 }
763
764 MemoryPool(const MemoryPool&) = delete;
765 MemoryPool& operator=(const MemoryPool&) = delete;
766
767 MemoryPool(MemoryPool&& other) noexcept : fCount(other.fCount),
768 fPool(std::move(other.fPool)) {
769 other.fCount = 0;
770 }
771
772 MemoryPool& operator=(MemoryPool&& other) noexcept {
773 // Since `this` may contain instances that need to be deleted, we can't
774 // just throw them away and replace them with `other`. The normal way of
775 // dealing with this in C++ is to swap `this` and `other`, rather than
776 // simply overwrite: the destruction of `other` can then take care of
777 // running MemoryPool::~MemoryPool() over the still-to-be-deallocated
778 // instances.
779 std::swap(fCount, other.fCount);
780 std::swap(fPool, other.fPool);
781 return *this;
782 }
783
784 /**
785 * Creates a new object of typename T, by forwarding any and all arguments
786 * to the typename T constructor.
787 *
788 * @param args Arguments to be forwarded to the typename T constructor.
789 * @return A pointer to the newly created object, or nullptr on error.
790 */
791 template<typename... Args>
792 T* create(Args&&... args) {
793 int32_t capacity = fPool.getCapacity();
794 if (fCount == capacity &&
795 fPool.resize(capacity == stackCapacity ? 4 * capacity : 2 * capacity,
796 capacity) == nullptr) {
797 return nullptr;
798 }
799 return fPool[fCount++] = new T(std::forward<Args>(args)...);
800 }
801
802 template <typename... Args>
803 T* createAndCheckErrorCode(UErrorCode &status, Args &&... args) {
804 if (U_FAILURE(status)) {
805 return nullptr;
806 }
807 T *pointer = this->create(args...);
808 if (U_SUCCESS(status) && pointer == nullptr) {
809 status = U_MEMORY_ALLOCATION_ERROR;
810 }
811 return pointer;
812 }
813
814 /**
815 * @return Number of elements that have been allocated.
816 */
817 int32_t count() const {
818 return fCount;
819 }
820
821protected:
822 int32_t fCount;
823 MaybeStackArray<T*, stackCapacity> fPool;
824};
825
826/**
827 * An internal Vector-like implementation based on MemoryPool.
828 *
829 * Heap-allocates each element and stores pointers.
830 *
831 * To append an item to the vector, use emplaceBack.
832 *
833 * MaybeStackVector<MyType> vector;
834 * MyType* element = vector.emplaceBack();
835 * if (!element) {
836 * status = U_MEMORY_ALLOCATION_ERROR;
837 * }
838 * // do stuff with element
839 *
840 * To loop over the vector, use a for loop with indices:
841 *
842 * for (int32_t i = 0; i < vector.length(); i++) {
843 * MyType* element = vector[i];
844 * }
845 */
846template<typename T, int32_t stackCapacity = 8>
847class MaybeStackVector : protected MemoryPool<T, stackCapacity> {
848public:
849 template<typename... Args>
850 T* emplaceBack(Args&&... args) {
851 return this->create(args...);
852 }
853
854 template <typename... Args>
855 T *emplaceBackAndCheckErrorCode(UErrorCode &status, Args &&... args) {
856 return this->createAndCheckErrorCode(status, args...);
857 }
858
859 int32_t length() const {
860 return this->fCount;
861 }
862
863 T** getAlias() {
864 return this->fPool.getAlias();
865 }
866
867 const T *const *getAlias() const {
868 return this->fPool.getAlias();
869 }
870
871 /**
872 * Array item access (read-only).
873 * No index bounds check.
874 * @param i array index
875 * @return reference to the array item
876 */
877 const T* operator[](ptrdiff_t i) const {
878 return this->fPool[i];
879 }
880
881 /**
882 * Array item access (writable).
883 * No index bounds check.
884 * @param i array index
885 * @return reference to the array item
886 */
887 T* operator[](ptrdiff_t i) {
888 return this->fPool[i];
889 }
890};
891
892
893U_NAMESPACE_END}
894
895#endif /* __cplusplus */
896#endif /* CMEMORY_H */

/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/unicode/localpointer.h

1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/*
4*******************************************************************************
5*
6* Copyright (C) 2009-2016, International Business Machines
7* Corporation and others. All Rights Reserved.
8*
9*******************************************************************************
10* file name: localpointer.h
11* encoding: UTF-8
12* tab size: 8 (not used)
13* indentation:4
14*
15* created on: 2009nov13
16* created by: Markus W. Scherer
17*/
18
19#ifndef __LOCALPOINTER_H__
20#define __LOCALPOINTER_H__
21
22/**
23 * \file
24 * \brief C++ API: "Smart pointers" for use with and in ICU4C C++ code.
25 *
26 * These classes are inspired by
27 * - std::auto_ptr
28 * - boost::scoped_ptr & boost::scoped_array
29 * - Taligent Safe Pointers (TOnlyPointerTo)
30 *
31 * but none of those provide for all of the goals for ICU smart pointers:
32 * - Smart pointer owns the object and releases it when it goes out of scope.
33 * - No transfer of ownership via copy/assignment to reduce misuse. Simpler & more robust.
34 * - ICU-compatible: No exceptions.
35 * - Need to be able to orphan/release the pointer and its ownership.
36 * - Need variants for normal C++ object pointers, C++ arrays, and ICU C service objects.
37 *
38 * For details see https://icu.unicode.org/design/cpp/scoped_ptr
39 */
40
41#include "unicode/utypes.h"
42
43#if U_SHOW_CPLUSPLUS_API1
44
45#include <memory>
46
47U_NAMESPACE_BEGINnamespace icu_78 {
48
49/**
50 * "Smart pointer" base class; do not use directly: use LocalPointer etc.
51 *
52 * Base class for smart pointer classes that do not throw exceptions.
53 *
54 * Do not use this base class directly, since it does not delete its pointer.
55 * A subclass must implement methods that delete the pointer:
56 * Destructor and adoptInstead().
57 *
58 * There is no operator T *() provided because the programmer must decide
59 * whether to use getAlias() (without transfer of ownership) or orphan()
60 * (with transfer of ownership and NULLing of the pointer).
61 *
62 * @see LocalPointer
63 * @see LocalArray
64 * @see U_DEFINE_LOCAL_OPEN_POINTER
65 * @stable ICU 4.4
66 */
67template<typename T>
68class LocalPointerBase {
69public:
70 // No heap allocation. Use only on the stack.
71 static void* U_EXPORT2 operator new(size_t) = delete;
72 static void* U_EXPORT2 operator new[](size_t) = delete;
73 static void* U_EXPORT2 operator new(size_t, void*) = delete;
74
75 /**
76 * Constructor takes ownership.
77 * @param p simple pointer to an object that is adopted
78 * @stable ICU 4.4
79 */
80 explicit LocalPointerBase(T *p=nullptr) : ptr(p) {}
7
Null pointer value stored to 'languageTypes.ptr'
81 /**
82 * Destructor deletes the object it owns.
83 * Subclass must override: Base class does nothing.
84 * @stable ICU 4.4
85 */
86 ~LocalPointerBase() { /* delete ptr; */ }
87 /**
88 * nullptr check.
89 * @return true if ==nullptr
90 * @stable ICU 4.4
91 */
92 UBool isNull() const { return ptr==nullptr; }
93 /**
94 * nullptr check.
95 * @return true if !=nullptr
96 * @stable ICU 4.4
97 */
98 UBool isValid() const { return ptr!=nullptr; }
99 /**
100 * Comparison with a simple pointer, so that existing code
101 * with ==nullptr need not be changed.
102 * @param other simple pointer for comparison
103 * @return true if this pointer value equals other
104 * @stable ICU 4.4
105 */
106 bool operator==(const T *other) const { return ptr==other; }
107 /**
108 * Comparison with a simple pointer, so that existing code
109 * with !=nullptr need not be changed.
110 * @param other simple pointer for comparison
111 * @return true if this pointer value differs from other
112 * @stable ICU 4.4
113 */
114 bool operator!=(const T *other) const { return ptr!=other; }
115 /**
116 * Access without ownership change.
117 * @return the pointer value
118 * @stable ICU 4.4
119 */
120 T *getAlias() const { return ptr; }
121 /**
122 * Access without ownership change.
123 * @return the pointer value as a reference
124 * @stable ICU 4.4
125 */
126 T &operator*() const { return *ptr; }
127 /**
128 * Access without ownership change.
129 * @return the pointer value
130 * @stable ICU 4.4
131 */
132 T *operator->() const { return ptr; }
133 /**
134 * Gives up ownership; the internal pointer becomes nullptr.
135 * @return the pointer value;
136 * caller becomes responsible for deleting the object
137 * @stable ICU 4.4
138 */
139 T *orphan() {
140 T *p=ptr;
141 ptr=nullptr;
142 return p;
143 }
144 /**
145 * Deletes the object it owns,
146 * and adopts (takes ownership of) the one passed in.
147 * Subclass must override: Base class does not delete the object.
148 * @param p simple pointer to an object that is adopted
149 * @stable ICU 4.4
150 */
151 void adoptInstead(T *p) {
152 // delete ptr;
153 ptr=p;
154 }
155protected:
156 /**
157 * Actual pointer.
158 * @internal
159 */
160 T *ptr;
161private:
162 // No comparison operators with other LocalPointerBases.
163 bool operator==(const LocalPointerBase<T> &other) = delete;
164 bool operator!=(const LocalPointerBase<T> &other) = delete;
165 // No ownership sharing: No copy constructor, no assignment operator.
166 LocalPointerBase(const LocalPointerBase<T> &other) = delete;
167 void operator=(const LocalPointerBase<T> &other) = delete;
168};
169
170/**
171 * "Smart pointer" class, deletes objects via the standard C++ delete operator.
172 * For most methods see the LocalPointerBase base class.
173 *
174 * Usage example:
175 * \code
176 * LocalPointer<UnicodeString> s(new UnicodeString((UChar32)0x50005));
177 * int32_t length=s->length(); // 2
178 * char16_t lead=s->charAt(0); // 0xd900
179 * if(some condition) { return; } // no need to explicitly delete the pointer
180 * s.adoptInstead(new UnicodeString((char16_t)0xfffc));
181 * length=s->length(); // 1
182 * // no need to explicitly delete the pointer
183 * \endcode
184 *
185 * @see LocalPointerBase
186 * @stable ICU 4.4
187 */
188template<typename T>
189class LocalPointer : public LocalPointerBase<T> {
190public:
191 using LocalPointerBase<T>::operator*;
192 using LocalPointerBase<T>::operator->;
193 /**
194 * Constructor takes ownership.
195 * @param p simple pointer to an object that is adopted
196 * @stable ICU 4.4
197 */
198 explicit LocalPointer(T *p=nullptr) : LocalPointerBase<T>(p) {}
199 /**
200 * Constructor takes ownership and reports an error if nullptr.
201 *
202 * This constructor is intended to be used with other-class constructors
203 * that may report a failure UErrorCode,
204 * so that callers need to check only for U_FAILURE(errorCode)
205 * and not also separately for isNull().
206 *
207 * @param p simple pointer to an object that is adopted
208 * @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR
209 * if p==nullptr and no other failure code had been set
210 * @stable ICU 55
211 */
212 LocalPointer(T *p, UErrorCode &errorCode) : LocalPointerBase<T>(p) {
213 if(p==nullptr && U_SUCCESS(errorCode)) {
214 errorCode=U_MEMORY_ALLOCATION_ERROR;
215 }
216 }
217 /**
218 * Move constructor, leaves src with isNull().
219 * @param src source smart pointer
220 * @stable ICU 56
221 */
222 LocalPointer(LocalPointer<T> &&src) noexcept : LocalPointerBase<T>(src.ptr) {
223 src.ptr=nullptr;
224 }
225
226 /**
227 * Constructs a LocalPointer from a C++11 std::unique_ptr.
228 * The LocalPointer steals the object owned by the std::unique_ptr.
229 *
230 * This constructor works via move semantics. If your std::unique_ptr is
231 * in a local variable, you must use std::move.
232 *
233 * @param p The std::unique_ptr from which the pointer will be stolen.
234 * @stable ICU 64
235 */
236 explicit LocalPointer(std::unique_ptr<T> &&p)
237 : LocalPointerBase<T>(p.release()) {}
238
239 /**
240 * Destructor deletes the object it owns.
241 * @stable ICU 4.4
242 */
243 ~LocalPointer() {
244 delete LocalPointerBase<T>::ptr;
245 }
246 /**
247 * Move assignment operator, leaves src with isNull().
248 * The behavior is undefined if *this and src are the same object.
249 * @param src source smart pointer
250 * @return *this
251 * @stable ICU 56
252 */
253 LocalPointer<T> &operator=(LocalPointer<T> &&src) noexcept {
254 delete LocalPointerBase<T>::ptr;
255 LocalPointerBase<T>::ptr=src.ptr;
256 src.ptr=nullptr;
257 return *this;
258 }
259
260 /**
261 * Move-assign from an std::unique_ptr to this LocalPointer.
262 * Steals the pointer from the std::unique_ptr.
263 *
264 * @param p The std::unique_ptr from which the pointer will be stolen.
265 * @return *this
266 * @stable ICU 64
267 */
268 LocalPointer<T> &operator=(std::unique_ptr<T> &&p) noexcept {
269 adoptInstead(p.release());
270 return *this;
271 }
272
273 /**
274 * Swap pointers.
275 * @param other other smart pointer
276 * @stable ICU 56
277 */
278 void swap(LocalPointer<T> &other) noexcept {
279 T *temp=LocalPointerBase<T>::ptr;
280 LocalPointerBase<T>::ptr=other.ptr;
281 other.ptr=temp;
282 }
283 /**
284 * Non-member LocalPointer swap function.
285 * @param p1 will get p2's pointer
286 * @param p2 will get p1's pointer
287 * @stable ICU 56
288 */
289 friend inline void swap(LocalPointer<T> &p1, LocalPointer<T> &p2) noexcept {
290 p1.swap(p2);
291 }
292 /**
293 * Deletes the object it owns,
294 * and adopts (takes ownership of) the one passed in.
295 * @param p simple pointer to an object that is adopted
296 * @stable ICU 4.4
297 */
298 void adoptInstead(T *p) {
299 delete LocalPointerBase<T>::ptr;
300 LocalPointerBase<T>::ptr=p;
301 }
302 /**
303 * Deletes the object it owns,
304 * and adopts (takes ownership of) the one passed in.
305 *
306 * If U_FAILURE(errorCode), then the current object is retained and the new one deleted.
307 *
308 * If U_SUCCESS(errorCode) but the input pointer is nullptr,
309 * then U_MEMORY_ALLOCATION_ERROR is set,
310 * the current object is deleted, and nullptr is set.
311 *
312 * @param p simple pointer to an object that is adopted
313 * @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR
314 * if p==nullptr and no other failure code had been set
315 * @stable ICU 55
316 */
317 void adoptInsteadAndCheckErrorCode(T *p, UErrorCode &errorCode) {
318 if(U_SUCCESS(errorCode)) {
319 delete LocalPointerBase<T>::ptr;
320 LocalPointerBase<T>::ptr=p;
321 if(p==nullptr) {
322 errorCode=U_MEMORY_ALLOCATION_ERROR;
323 }
324 } else {
325 delete p;
326 }
327 }
328
329 /**
330 * Conversion operator to a C++11 std::unique_ptr.
331 * Disowns the object and gives it to the returned std::unique_ptr.
332 *
333 * This operator works via move semantics. If your LocalPointer is
334 * in a local variable, you must use std::move.
335 *
336 * @return An std::unique_ptr owning the pointer previously owned by this
337 * icu::LocalPointer.
338 * @stable ICU 64
339 */
340 operator std::unique_ptr<T> () && {
341 return std::unique_ptr<T>(LocalPointerBase<T>::orphan());
342 }
343};
344
345/**
346 * "Smart pointer" class, deletes objects via the C++ array delete[] operator.
347 * For most methods see the LocalPointerBase base class.
348 * Adds operator[] for array item access.
349 *
350 * Usage example:
351 * \code
352 * LocalArray<UnicodeString> a(new UnicodeString[2]);
353 * a[0].append((char16_t)0x61);
354 * if(some condition) { return; } // no need to explicitly delete the array
355 * a.adoptInstead(new UnicodeString[4]);
356 * a[3].append((char16_t)0x62).append((char16_t)0x63).reverse();
357 * // no need to explicitly delete the array
358 * \endcode
359 *
360 * @see LocalPointerBase
361 * @stable ICU 4.4
362 */
363template<typename T>
364class LocalArray : public LocalPointerBase<T> {
365public:
366 using LocalPointerBase<T>::operator*;
367 using LocalPointerBase<T>::operator->;
368 /**
369 * Constructor takes ownership.
370 * @param p simple pointer to an array of T objects that is adopted
371 * @stable ICU 4.4
372 */
373 explicit LocalArray(T *p=nullptr) : LocalPointerBase<T>(p) {}
374 /**
375 * Constructor takes ownership and reports an error if nullptr.
376 *
377 * This constructor is intended to be used with other-class constructors
378 * that may report a failure UErrorCode,
379 * so that callers need to check only for U_FAILURE(errorCode)
380 * and not also separately for isNull().
381 *
382 * @param p simple pointer to an array of T objects that is adopted
383 * @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR
384 * if p==nullptr and no other failure code had been set
385 * @stable ICU 56
386 */
387 LocalArray(T *p, UErrorCode &errorCode) : LocalPointerBase<T>(p) {
388 if(p==nullptr && U_SUCCESS(errorCode)) {
389 errorCode=U_MEMORY_ALLOCATION_ERROR;
390 }
391 }
392 /**
393 * Move constructor, leaves src with isNull().
394 * @param src source smart pointer
395 * @stable ICU 56
396 */
397 LocalArray(LocalArray<T> &&src) noexcept : LocalPointerBase<T>(src.ptr) {
398 src.ptr=nullptr;
399 }
400
401 /**
402 * Constructs a LocalArray from a C++11 std::unique_ptr of an array type.
403 * The LocalPointer steals the array owned by the std::unique_ptr.
404 *
405 * This constructor works via move semantics. If your std::unique_ptr is
406 * in a local variable, you must use std::move.
407 *
408 * @param p The std::unique_ptr from which the array will be stolen.
409 * @stable ICU 64
410 */
411 explicit LocalArray(std::unique_ptr<T[]> &&p)
412 : LocalPointerBase<T>(p.release()) {}
413
414 /**
415 * Destructor deletes the array it owns.
416 * @stable ICU 4.4
417 */
418 ~LocalArray() {
419 delete[] LocalPointerBase<T>::ptr;
420 }
421 /**
422 * Move assignment operator, leaves src with isNull().
423 * The behavior is undefined if *this and src are the same object.
424 * @param src source smart pointer
425 * @return *this
426 * @stable ICU 56
427 */
428 LocalArray<T> &operator=(LocalArray<T> &&src) noexcept {
429 delete[] LocalPointerBase<T>::ptr;
430 LocalPointerBase<T>::ptr=src.ptr;
431 src.ptr=nullptr;
432 return *this;
433 }
434
435 /**
436 * Move-assign from an std::unique_ptr to this LocalPointer.
437 * Steals the array from the std::unique_ptr.
438 *
439 * @param p The std::unique_ptr from which the array will be stolen.
440 * @return *this
441 * @stable ICU 64
442 */
443 LocalArray<T> &operator=(std::unique_ptr<T[]> &&p) noexcept {
444 adoptInstead(p.release());
445 return *this;
446 }
447
448 /**
449 * Swap pointers.
450 * @param other other smart pointer
451 * @stable ICU 56
452 */
453 void swap(LocalArray<T> &other) noexcept {
454 T *temp=LocalPointerBase<T>::ptr;
455 LocalPointerBase<T>::ptr=other.ptr;
456 other.ptr=temp;
457 }
458 /**
459 * Non-member LocalArray swap function.
460 * @param p1 will get p2's pointer
461 * @param p2 will get p1's pointer
462 * @stable ICU 56
463 */
464 friend inline void swap(LocalArray<T> &p1, LocalArray<T> &p2) noexcept {
465 p1.swap(p2);
466 }
467 /**
468 * Deletes the array it owns,
469 * and adopts (takes ownership of) the one passed in.
470 * @param p simple pointer to an array of T objects that is adopted
471 * @stable ICU 4.4
472 */
473 void adoptInstead(T *p) {
474 delete[] LocalPointerBase<T>::ptr;
475 LocalPointerBase<T>::ptr=p;
476 }
477 /**
478 * Deletes the array it owns,
479 * and adopts (takes ownership of) the one passed in.
480 *
481 * If U_FAILURE(errorCode), then the current array is retained and the new one deleted.
482 *
483 * If U_SUCCESS(errorCode) but the input pointer is nullptr,
484 * then U_MEMORY_ALLOCATION_ERROR is set,
485 * the current array is deleted, and nullptr is set.
486 *
487 * @param p simple pointer to an array of T objects that is adopted
488 * @param errorCode in/out UErrorCode, set to U_MEMORY_ALLOCATION_ERROR
489 * if p==nullptr and no other failure code had been set
490 * @stable ICU 56
491 */
492 void adoptInsteadAndCheckErrorCode(T *p, UErrorCode &errorCode) {
493 if(U_SUCCESS(errorCode)) {
494 delete[] LocalPointerBase<T>::ptr;
495 LocalPointerBase<T>::ptr=p;
496 if(p==nullptr) {
497 errorCode=U_MEMORY_ALLOCATION_ERROR;
498 }
499 } else {
500 delete[] p;
501 }
502 }
503 /**
504 * Array item access (writable).
505 * No index bounds check.
506 * @param i array index
507 * @return reference to the array item
508 * @stable ICU 4.4
509 */
510 T &operator[](ptrdiff_t i) const { return LocalPointerBase<T>::ptr[i]; }
511
512 /**
513 * Conversion operator to a C++11 std::unique_ptr.
514 * Disowns the object and gives it to the returned std::unique_ptr.
515 *
516 * This operator works via move semantics. If your LocalPointer is
517 * in a local variable, you must use std::move.
518 *
519 * @return An std::unique_ptr owning the pointer previously owned by this
520 * icu::LocalPointer.
521 * @stable ICU 64
522 */
523 operator std::unique_ptr<T[]> () && {
524 return std::unique_ptr<T[]>(LocalPointerBase<T>::orphan());
525 }
526};
527
528/**
529 * \def U_DEFINE_LOCAL_OPEN_POINTER
530 * "Smart pointer" definition macro, deletes objects via the closeFunction.
531 * Defines a subclass of LocalPointerBase which works just
532 * like LocalPointer<Type> except that this subclass will use the closeFunction
533 * rather than the C++ delete operator.
534 *
535 * Usage example:
536 * \code
537 * LocalUCaseMapPointer csm(ucasemap_open(localeID, options, &errorCode));
538 * utf8OutLength=ucasemap_utf8ToLower(csm.getAlias(),
539 * utf8Out, (int32_t)sizeof(utf8Out),
540 * utf8In, utf8InLength, &errorCode);
541 * if(U_FAILURE(errorCode)) { return; } // no need to explicitly delete the UCaseMap
542 * \endcode
543 *
544 * @see LocalPointerBase
545 * @see LocalPointer
546 * @stable ICU 4.4
547 */
548#define U_DEFINE_LOCAL_OPEN_POINTER(LocalPointerClassName, Type, closeFunction)using LocalPointerClassName = internal::LocalOpenPointer<Type
, closeFunction>
\
549 using LocalPointerClassName = internal::LocalOpenPointer<Type, closeFunction>
550
551#ifndef U_IN_DOXYGEN
552namespace internal {
553/**
554 * Implementation, do not use directly: use U_DEFINE_LOCAL_OPEN_POINTER.
555 *
556 * @see U_DEFINE_LOCAL_OPEN_POINTER
557 * @internal
558 */
559template <typename Type, auto closeFunction>
560class LocalOpenPointer : public LocalPointerBase<Type> {
561 using LocalPointerBase<Type>::ptr;
562public:
563 using LocalPointerBase<Type>::operator*;
564 using LocalPointerBase<Type>::operator->;
565 explicit LocalOpenPointer(Type *p=nullptr) : LocalPointerBase<Type>(p) {}
566 LocalOpenPointer(LocalOpenPointer &&src) noexcept
567 : LocalPointerBase<Type>(src.ptr) {
568 src.ptr=nullptr;
569 }
570 /* TODO: Be agnostic of the deleter function signature from the user-provided std::unique_ptr? */
571 explicit LocalOpenPointer(std::unique_ptr<Type, decltype(closeFunction)> &&p)
572 : LocalPointerBase<Type>(p.release()) {}
573 ~LocalOpenPointer() { if (ptr != nullptr) { closeFunction(ptr); } }
574 LocalOpenPointer &operator=(LocalOpenPointer &&src) noexcept {
575 if (ptr != nullptr) { closeFunction(ptr); }
576 LocalPointerBase<Type>::ptr=src.ptr;
577 src.ptr=nullptr;
578 return *this;
579 }
580 /* TODO: Be agnostic of the deleter function signature from the user-provided std::unique_ptr? */
581 LocalOpenPointer &operator=(std::unique_ptr<Type, decltype(closeFunction)> &&p) {
582 adoptInstead(p.release());
583 return *this;
584 }
585 void swap(LocalOpenPointer &other) noexcept {
586 Type *temp=LocalPointerBase<Type>::ptr;
587 LocalPointerBase<Type>::ptr=other.ptr;
588 other.ptr=temp;
589 }
590 friend inline void swap(LocalOpenPointer &p1, LocalOpenPointer &p2) noexcept {
591 p1.swap(p2);
592 }
593 void adoptInstead(Type *p) {
594 if (ptr != nullptr) { closeFunction(ptr); }
595 ptr=p;
596 }
597 operator std::unique_ptr<Type, decltype(closeFunction)> () && {
598 return std::unique_ptr<Type, decltype(closeFunction)>(LocalPointerBase<Type>::orphan(), closeFunction);
599 }
600};
601} // namespace internal
602#endif
603
604U_NAMESPACE_END}
605
606#endif /* U_SHOW_CPLUSPLUS_API */
607#endif /* __LOCALPOINTER_H__ */